Как получить дочерний элемент по имени класса?

131

Я пытаюсь получить дочерний диапазон с class = 4. Вот пример элемента:

<div id="test">
 <span class="one"></span>
 <span class="two"></span>
 <span class="three"></span>
 <span class="four"></span>
</div>

У меня есть доступные инструменты: JS и YUI2. Я могу сделать что-то вроде этого:

doc = document.getElementById('test');
notes = doc.getElementsByClassName('four');

//or

doc = YAHOO.util.Dom.get('#test');
notes = doc.getElementsByClassName('four');

Они не работают в IE. Я получаю сообщение об ошибке, что объект (doc) не поддерживает этот метод или свойство (getElementsByClassName). Я пробовал несколько примеров кроссбраузерных реализаций getElementsByClassName, но я не мог заставить их работать и по-прежнему получал эту ошибку.

Я думаю, что мне нужен кроссбраузер getElementsByClassName, или мне нужно использовать doc.getElementsByTagName ('span') и перебирать, пока не найду класс 4. Я не знаю, как это сделать.

spyderman4g63
источник
Вот он: stackoverflow.com/questions/3808808/…
Мостар,
1
Как ни странно, более мощный querySelectorAllподдерживается IE 8+, тогда как getElementsByClassNameподдерживается только IE 9+. Если вы можете отказаться от IE 7, вы в безопасности querySelectorAll('.4'). Кстати, 4неверное имя класса.
Prinzhorn
@paritybit этот вопрос не работает, потому что он все еще использует getElementsByClassName, а более старая версия IE, похоже, не поддерживает это. изменить: мне жаль, что он использует имя тега. Это может сработать.
spyderman4g63
@Prinzhorn У меня по какой-то причине нет утилиты выбора YUI2 в этом продукте.
spyderman4g63
@ spyderman4g63 Я не говорил ни о чем конкретном YUI2. document.querySelectorAllявляется DOM и не имеет ничего общего с YUI
Prinzhorn

Ответы:

85

Используйте doc.childNodesдля перебора каждого из них span, а затем отфильтруйте тот, который classNameравен 4:

var doc = document.getElementById("test");
var notes = null;
for (var i = 0; i < doc.childNodes.length; i++) {
    if (doc.childNodes[i].className == "4") {
      notes = doc.childNodes[i];
      break;
    }        
}

Жоао Силва
источник
1
Проблема в том, что getElementsByClassName не работает в IE8.
spyderman4g63
7
Этот код не работает, если элемент имеет более одного класса. Например: если вы ищете элементы с классом «один», а ваши элементы имеют класс «один, два, три», эта функция их не найдет.
Fran Verona
3
@FranVerona Просто из любопытства не решит ли простой "indexOf ('className')> -1" проблему нескольких классов?
Джеймс Пулозе,
2
@JamesPoulose, это не так . Подумайте о том, чтобы задать для элемента два класса: маленький и большой. thatElement.className вернет строку, равную «маленький больше». Если вы ищете класс под названием «большой», то скажите, что myElement.className.indexOf («большой») выдаст что-то, не равное отрицательному 1, несмотря на то, что он фактически не является частью класса. Если у вас есть 100% -ный контроль над именами классов, такое исправление будет работать, это просто не гарантируется, особенно когда вы пишете код, который будет взаимодействовать с кодом, над которым вы не имеете полного контроля.
deadboy
Вы должны использовать соответствие регулярному выражению classNameпримерно так: if(doc.childNode[i].className.match("\s*" + search_class + "\s*")где переменная search_class- это имя класса, который вы ищете.
WebWanderer
130

Используйте querySelector и querySelectorAll

var testContainer = document.querySelector('#test');
var fourChildNode = testContainer.querySelector('.four');

IE9 и выше

;)

Альберто Клар Рассол
источник
Спасибо за умное решение!
Тай ЦзиньЮань,
1
Это проверяет всех потомков, а не только детей.
SUM1,
47

Принятый ответ проверяет только непосредственных потомков. Часто мы ищем потомков с таким именем класса.

Кроме того, иногда нам нужен любой дочерний элемент , содержащий className.

Например: <div class="img square"></div>должно соответствовать поиску по className «img», даже если это точное имя className не «img».

Вот решение обеих этих проблем:

Найдите первый экземпляр дочернего элемента с классом className

   function findFirstChildByClass(element, className) {
        var foundElement = null, found;
        function recurse(element, className, found) {
            for (var i = 0; i < element.childNodes.length && !found; i++) {
                var el = element.childNodes[i];
                var classes = el.className != undefined? el.className.split(" ") : [];
                for (var j = 0, jl = classes.length; j < jl; j++) {
                    if (classes[j] == className) {
                        found = true;
                        foundElement = element.childNodes[i];
                        break;
                    }
                }
                if(found)
                    break;
                recurse(element.childNodes[i], className, found);
            }
        }
        recurse(element, className, false);
        return foundElement;
    }
Оги Гарднер
источник
7
Нет. Я не хочу всех потомков, только ребенка, как задан вопрос.
Пол Дрейпер
14
Дракончик. Ваш комментарий был необходим. Рад, что помог 13 другим людям, которые (как и я) часто искали похожие ответы, чтобы решить свою похожую, но, возможно, более сложную или общую проблему.
Оги Гарднер,
5
tnx, я думаю, что этот ответ подходит для других случаев использования
Бобан Стояновски
Для записи, у меня были оба варианта использования, потомки немного чаще, но я пришел сюда для дочернего варианта использования.
SUM1,
14

Используйте element.querySelector (). Предположим: myElement - это родительский элемент, который у вас уже есть. sonClassName - это класс ребенка, которого вы ищете.

let child = myElement.querySelector('.sonClassName');

Для получения дополнительной информации посетите: https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector.

Хамза Дахмун
источник
Почему-то let child = myElement.querySelector('sonClassName');в моем случае пришлось использовать
малат
Но вы должны добавить точку рядом с именем класса querySelector ('. SonClassName'), иначе он будет обрабатывать его, поскольку sonClassName - это имя тега, а не имя класса
Хамза Дахмун,
10

Вы можете попробовать:

notes = doc.querySelectorAll('.4');

или

notes = doc.getElementsByTagName('*');
for (var i = 0; i < notes.length; i++) { 
    if (notes[i].getAttribute('class') == '4') {
    }
}
Korikulum
источник
1
Мне было интересно, получили ли браузеры наконец эту функциональность, не требуя чего-то вроде jQuery, и был рад это видеть. Если кому-то интересно, похоже, что он поддерживается в большинстве современных браузеров: developer.mozilla.org/en-US/docs/Web/API/Document/…
Лирон Яхдав
8

Мне кажется, что вы хотите четвертый пролет. Если да, вы можете просто сделать это:

document.getElementById("test").childNodes[3]

или

document.getElementById("test").getElementsByTagName("span")[3]

Этот последний гарантирует, что нет никаких скрытых узлов, которые могут его испортить.

Кристиан Йоргенсен
источник
1
Спасибо, но количество дочерних элементов разное.
spyderman4g63,
1
Мне казалось, что вопрос состоит в том, чтобы всегда иметь четвертого ребенка, что привело к моему ответу. Я, конечно, согласен с тем, что функция для получения любого типа элемента по любому индексу внутри другого элемента была бы лучше, но я не интерпретировал вопрос таким образом ... читая вопрос еще раз, я вижу, что я совершенно неправильно понял: - )
Кристиан Йоргенсен
TagNameЭто оно! Так просто.
Jacksonkr
Я часто использую эту технику для получения первого элемента, например, document.getElementById ("test"). ChildNodes [0]
Луиза Эгглтон
@LouiseEggleton есть .firstChildи .firstChildElementхотя
YakovL
4

Но имейте в виду, что старые браузеры не поддерживают getElementsByClassName.

Тогда вы можете сделать

function getElementsByClassName(c,el){
    if(typeof el=='string'){el=document.getElementById(el);}
    if(!el){el=document;}
    if(el.getElementsByClassName){return el.getElementsByClassName(c);}
    var arr=[],
        allEls=el.getElementsByTagName('*');
    for(var i=0;i<allEls.length;i++){
        if(allEls[i].className.split(' ').indexOf(c)>-1){arr.push(allEls[i])}
    }
    return arr;
}
getElementsByClassName('4','test')[0];

Кажется, это работает, но имейте в виду, что класс HTML

  • Должен начинаться с буквы: AZ или az
  • Может сопровождаться буквами (A-Za-z), цифрами (0-9), дефисами ("-") и подчеркиванием ("_").
Ориоль
источник
3

Используйте имя идентификатора getElementByIdбез #знака перед ним. Затем вы можете использовать spanдочерние узлы getElementsByTagNameи перебирать их, чтобы найти узел с правильным классом:

var doc = document.getElementById('test');

var c = doc.getElementsByTagName('span');

var e = null;
for (var i = 0; i < c.length; i++) {
    if (c[i].className == '4') {
        e = c[i];
        break;
    }
}

if (e != null) {
    alert(e.innerHTML);
}

Демо: http://jsfiddle.net/Guffa/xB62U/

Guffa
источник
Извините, это # ​​была опечатка. Я могу выбрать контейнер div, но не могу выбрать дочерние элементы по имени тега, потому что эта функция не работает в ie8.
spyderman4g63
@ spyderman4g63: getElementsByTagNameМетод работает в IE 8. Он поддерживается еще в IE 5.5. developer.mozilla.org/en-US/docs/DOM/…
Guffa
3

На мой взгляд, каждый раз, когда вы можете, вы должны использовать Array и его методы. Они намного, намного быстрее, чем цикл по всей DOM / оболочке или вставка материала в пустой массив. Большинство представленных здесь решений можно назвать наивными, как описано здесь (кстати, отличная статья):

https://medium.com/@chuckdries/traversing-the-dom-with-filter-map-and-arrow-functions-1417d326d2bc

Мое решение : (предварительный просмотр на Codepen: https://codepen.io/Nikolaus91/pen/wEGEYe )

const wrapper = document.getElementById('test') // take a wrapper by ID -> fastest
const itemsArray = Array.from(wrapper.children) // make Array from his children

const pickOne = itemsArray.map(item => { // loop over his children using .map() --> see MDN for more
   if(item.classList.contains('four')) // we place a test where we determine our choice
     item.classList.add('the-chosen-one') // your code here
})

источник
2

Современное решение

const context = document.getElementById('context');
const selected = context.querySelectorAll(':scope > div');

документация

Billizzard
источник
1

Я сделаю это с помощью jquery примерно так ...

var targetchild = $ ("# тест"). children (). find ("span.four");

Марселус Чиа
источник
14
вопрос касается javascript, а не jQuery
Jya
1

Вот относительно простое рекурсивное решение. Думаю, здесь уместен поиск в ширину. Это вернет первый элемент, соответствующий найденному классу.

function getDescendantWithClass(element, clName) {
    var children = element.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].className &&
            children[i].className.split(' ').indexOf(clName) >= 0) {
            return children[i];
         }
     }
     for (var i = 0; i < children.length; i++) {
         var match = getDescendantWithClass(children[i], clName);
         if (match !== null) {
             return match;
         }
     }
     return null;
}
Иер
источник
1

Вы можете получить родительский класс, добавив строку ниже. Если бы у вас был id, было бы проще с getElementById. Тем не менее,

var parentNode = document.getElementsByClassName("progress__container")[0];

Затем вы можете использовать querySelectorAllродительский <div>элемент, чтобы получить все соответствующие divs с классом.progress__marker

var progressNodes = progressContainer.querySelectorAll('.progress__marker');

querySelectorAllбудет получать каждый divс классомprogress__marker

Усама Хавар
источник
0

Обновление ES6 за июнь 2018 г.

    const doc = document.getElementById('test');
    let notes = null;
    for (const value of doc) {
        if (value.className === '4') {
            notes = value;
            break;
        }    
    }
Кристофер Григг
источник
-2

YUI2 имеет кроссбраузерную реализациюgetElementsByClassName .

Хэнк Гей
источник
Хм. Пример работает в IE8, но, возможно, он не работает в случае, если вы примените его к объекту. В моем случае устанавливается doc как объект yui dom, а затем используется doc.getElementsByClassName. Вот когда это терпит неудачу. edit: или, может быть, я не понимаю, как этот объект работает, и это просто обычный объект dom?
spyderman4g63
@ spyderman4g63 версия YUI getElementsByClassName- это метод YAHOO.util.Dom. В вашем случае звонок будет выглядеть примерно так YAHOO.util.Dom.getElementsByClassName('four', 'span', 'test'). Вероятно, вам следует прочитать документацию, чтобы получить более подробное представление о том, что делает этот вызов. Краткая версия заключается в том, что теперь он будет искать spanэлементы с классом fourпод элементом DOM с testего id.
Hank Gay,
@ spyderman4g63 Да, результатом getвызова является просто элемент DOM. YUI не использует подход, основанный на оптовой продаже всего объекта, зависящего от фреймворка, как это делает что-то вроде jQuery, и при этом он не использует собственные объекты, такие как Prototype, как обезьяна-патч (сделал? Я не перепутал с Protoype навсегда). В этом отношении YUI больше похож на Додзё.
Hank Gay,
-3

Вот как я это сделал с помощью селекторов YUI. Благодаря предложению Хэнка Гэя.

notes = YAHOO.util.Dom.getElementsByClassName('four','span','test');

где four = имя класса, span = тип элемента / имя тега и test = родительский идентификатор.

spyderman4g63
источник