Использование querySelectorAll для получения прямых потомков - PullRequest
82 голосов
/ 10 сентября 2010

Я могу сделать это:

<div id="myDiv">
   <div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");

То есть я могу успешно получить все прямые потомки элемента myDiv, которые имеют класс .foo.

Проблема в том, что меня беспокоит то, что я должен включить #myDiv в селектор, потому что я выполняю запрос к элементу myDiv (поэтому он явно избыточен).

Я должен иметь возможность отключить #myDiv, но тогда селектор не является допустимым синтаксисом, поскольку он начинается с >.

Кто-нибудь знает, как написать селектор, который получает только прямые дочерние элементы элемента, на котором работает селектор?

Ответы [ 10 ]

110 голосов
/ 15 января 2014

Кто-нибудь знает, как написать селектор, который получает только прямые дочерние элементы элемента, на котором работает селектор?

Правильный способ записи селектора, который «укоренен» в текущем элементе, - использовать :scope.

var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");

Однако поддержка браузера ограничена , и вам понадобится прокладка, если вы хотите ее использовать. Для этой цели я создал scopedQuerySelectorShim .

39 голосов
/ 20 июня 2013

Хороший вопрос. В то время, когда об этом спрашивали, универсально реализованного способа выполнения «корневых запросов комбинатора» (как его назвал Джон Резиг ) не существовало.

Теперь введен псевдокласс : scope . Он не поддерживается в [pre-Chrominum] версиях Edge или IE, но Safari поддерживается уже несколько лет. Используя это, ваш код может стать:

let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");

Обратите внимание, что в некоторых случаях вы также можете пропустить .querySelectorAll и использовать другие старые добрые функции DOM API. Например, вместо myDiv.querySelectorAll(":scope > *") вы можете просто написать, например, myDiv.children.

В противном случае, если вы еще не можете положиться на :scope, я не могу придумать другой способ справиться с вашей ситуацией без добавления дополнительной настраиваемой логики фильтра (например, найти myDiv.getElementsByClassName("foo"), чья .parentNode === myDiv), и, очевидно, не идеал если вы пытаетесь поддерживать один путь кода, который на самом деле просто хочет взять произвольную строку селектора в качестве входных данных и список совпадений в качестве выходных! Но если, как и я, вы в конечном итоге задали этот вопрос просто потому, что застряли, думая, что «все, что у вас было, это молоток», не забывайте, что существует множество других инструментов, которые также предлагает DOM.

4 голосов
/ 29 июля 2013

Вот гибкий метод, написанный на vanilla JS, который позволяет запускать запрос селектора CSS только для прямых потомков элемента:

var count = 0;
function queryChildren(element, selector) {
  var id = element.id,
      guid = element.id = id || 'query_children_' + count++,
      attr = '#' + guid + ' > ',
      selector = attr + (selector + '').replace(',', ',' + attr, 'g');
  var result = element.parentNode.querySelectorAll(selector);
  if (!id) element.removeAttribute('id');
  return result;
}
2 голосов
/ 07 декабря 2012

если вы точно знаете, что элемент уникален (например, ваш случай с идентификатором):

myDiv.parentElement.querySelectorAll("#myDiv > .foo");

Для более «глобального» решения: (используйте matchSelector shim )

function getDirectChildren(elm, sel){
    var ret = [], i = 0, l = elm.childNodes.length;
    for (var i; i < l; ++i){
        if (elm.childNodes[i].matchesSelector(sel)){
            ret.push(elm.childNodes[i]);
        }
    }
    return ret;
}

, где elm - ваш родительский элемент, а sel - ваш селектор.Может быть полностью использован в качестве прототипа.

1 голос
/ 29 октября 2017

Следующее решение отличается от предложенных ранее и работает для меня.

Смысл в том, что вы сначала выбираете всех подходящих потомков, а затем отфильтровываете тех, которые не являются прямыми потомками. Дочерний является прямым потомком, если у него нет соответствующего родителя с тем же селектором.

function queryDirectChildren(parent, selector) {
    const nodes = parent.querySelectorAll(selector);
    const filteredNodes = [].slice.call(nodes).filter(n => 
        n.parentNode.closest(selector) === parent.closest(selector)
    );
    return filteredNodes;
}

НТН!

1 голос
/ 16 июня 2016

Я создал функцию для обработки этой ситуации, думал, что поделюсь ею.

getDirectDecendent(elem, selector, all){
    const tempID = randomString(10) //use your randomString function here.
    elem.dataset.tempid = tempID;

    let returnObj;
    if(all)
        returnObj = elem.parentElement.querySelectorAll(`[data-tempid="${tempID}"] > ${selector}`);
    else
        returnObj = elem.parentElement.querySelector(`[data-tempid="${tempID}"] > ${selector}`);

    elem.dataset.tempid = '';
    return returnObj;
}

По сути, вы делаете генерирование случайной строки (функция randomString здесь - это импортированный модуль npm,вы можете сделать свой собственный.) затем использовать эту случайную строку, чтобы гарантировать, что вы получите ожидаемый элемент в селекторе.Тогда вы можете использовать > после этого.

Причина, по которой я не использую атрибут id, заключается в том, что атрибут id уже может использоваться, и я не хочу его переопределять.

0 голосов
/ 30 августа 2018

Я хотел бы добавить, что вы можете расширить совместимость : scope , просто назначив временный атрибут текущему узлу.

let node = [...];
let result;

node.setAttribute("foo", "");
result = window.document.querySelectorAll("[foo] > .bar");
// And, of course, you can also use other combinators.
result = window.document.querySelectorAll("[foo] + .bar");
result = window.document.querySelectorAll("[foo] ~ .bar");
node.removeAttribute("foo");
0 голосов
/ 27 июня 2017

Ну, мы можем легко получить все прямые потомки элемента, используя childNodes, и мы можем выбрать предков с определенным классом с querySelectorAll, так что нетрудно представить, что мы могли бы создать новую функцию, которая получает оба и сравниваетthe two.

HTMLElement.prototype.queryDirectChildren = function(selector){
  var direct = [].slice.call(this.directNodes || []); // Cast to Array
  var queried = [].slice.call(this.querySelectorAll(selector) || []); // Cast to Array
  var both = [];
  // I choose to loop through the direct children because it is guaranteed to be smaller
  for(var i=0; i<direct.length; i++){
    if(queried.indexOf(direct[i])){
      both.push(direct[i]);
    }
  }
  return both;
}

Примечание. Это вернет массив узлов, а не NodeList.

Использование

 document.getElementById("myDiv").queryDirectChildren(".foo");
0 голосов
/ 25 июля 2016

Я бы пошел с

var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;
0 голосов
/ 10 ноября 2012

Я просто делаю это, даже не пытаясь это сделать. Будет ли это работать?

myDiv = getElementById("myDiv");
myDiv.querySelectorAll(this.id + " > .foo");

Дайте ему попробовать, может быть, это работает, а может и нет. Apolovies, но я не на компьютере, чтобы попробовать его (отвечает с моего iPhone).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...