Использование querySelectorAll для извлечения прямых потомков
Я могу это сделать:
<div id="myDiv">
<div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");
то есть, я могу успешно получить все прямые дети myDiv элемент, который имеет класс .foo.
проблема в том, что меня беспокоит, что я должен включать #myDiv в селекторе, потому что я запускаю запрос на myDiv элемент (так что явно избыточно).
Я должен быть в состоянии оставить #myDiv выкл, но тогда селектор не является законным синтаксисом, так как он начинается с a >.
кто-нибудь знает, как написать селектор, который получает только прямые потомки элемента, на котором работает селектор?
10 ответов:
хороший вопрос, но, как вы видите, нет никакого способа сделать "combinator rooted queries" как Джон уходит в отставку называет их.
однако, в некоторых случаях вы можете просто пропустить
.querySelectorAllи использовать другие старые добрые функции DOM API. Вместо неподдерживаемогоmyDiv.querySelectorAll("> *")вы можете просто написатьmyDiv.children, например.к сожалению, я не могу придумать способ справиться с вашей ситуацией без добавления дополнительной логики пользовательского фильтра (например, find
myDiv.getElementsByClassName("foo")чей.parentNode === myDiv), и, очевидно, не идеально, если вы пытаетесь поддерживать один путь кода, который действительно просто хочет взять произвольную строку селектора в качестве ввода и список совпадений в качестве вывода! Но если вы, как и я, задали этот вопрос просто потому, что вы застряли, думая: "все, что у вас было, это молоток", не забывайте, что есть множество другое инструменты, которые предлагает DOM.
кто-нибудь знает, как написать селектор, который получает только прямые потомки элемента, на котором работает селектор?
правильный способ записи селектора, который "укоренен" в текущий элемент, - использовать
:scope.var myDiv = getElementById("myDiv"); var fooEls = myDiv.querySelectorAll(":scope > .foo");,поддержка браузера ограничена и вам понадобится прокладка, если вы хотите ее использовать. Я построил scopedQuerySelectorShim для этого цель.
Примечание: The
:scopeпсевдо-класс является устаревшим. Тем не менее, он продолжает поддерживаться вquerySelector/querySelectorAll.
вот гибкий метод, написанный в 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; }
если вы точно знаете, что элемент уникален (например, ваш случай с идентификатором):
myDiv.parentElement.querySelectorAll("#myDiv > .foo");для более "глобального" решения: (используйте прокладка matchesSelector)
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- Это ваш выбор. Может быть полностью использован в качестве прототипа, а также.
Я создал функцию для обработки этой ситуации, думал, что поделюсь ею.
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 может уже используется и я не хочу, чтобы переопределить это.
Ну мы можем легко получить все прямые потомки элемента с помощью
childNodesи мы можем выбрать предков с определенным классом сquerySelectorAll, поэтому нетрудно представить, что мы могли бы создать новую функцию, которая получает оба и сравнивает их.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; }Примечание: это вернет массив узлов, а не список узлов.
использование
document.getElementById("myDiv").queryDirectChildren(".foo");
следующее решение отличается от тех, которые были предложены до сих пор, и работает для меня.
обоснование заключается в том, что вы сначала выбираете все соответствующие дочерние элементы, а затем отфильтровываете те, которые не являются прямыми дочерними элементами. Дочерний элемент является прямым дочерним элементом, если у него нет соответствующего родителя с тем же селектором.
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; }НТН!
Я хотел бы добавить, что вы можете продлить совместимости : 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");
Я просто делаю это, даже не пытаясь его. Сработает ли это?
myDiv = getElementById("myDiv"); myDiv.querySelectorAll(this.id + " > .foo");дайте ему попробовать, может быть, это работает, может быть, нет. Apolovies, но я сейчас не на компьютере, чтобы попробовать его (отвечая с моего iPhone).
Comments