选择器搜索直接子元素

我有一些类似 jquery 的函数:

function(elem) {
return $('> someselector', elem);
};

问题是我怎样才能对 querySelector()做同样的事情?

问题是 querySelector()中的 >选择器要求显式指定父选择器。有什么解决办法吗?

83422 次浏览

不行,没有选择器可以模拟你的起点。

JQuery 这样做的方式(更多是因为 qsa的行为方式不符合他们的喜好)是,他们检查 elem是否有 ID,如果没有,他们暂时添加一个 ID,然后创建一个完整的选择器字符串。

基本上你会这么做:

var sel = '> someselector';
var hadId = true;
if( !elem.id ) {
hadID = false;
elem.id = 'some_unique_value';
}


sel = '#' + elem.id + sel;


var result = document.querySelectorAll( sel );


if( !hadId ) {
elem.id = '';
}

这当然不是 jQuery 代码,但据我所知,这基本上就是它们的工作。不仅在这种情况下,而且在从嵌套元素的上下文运行选择器的任何情况下都是如此。

Sizzle 的源代码

声明

就个人而言,我会选择 Patrick dw 的答案,+ 1他的答案,我的答案是寻找替代解决方案。我不认为它应该被否决。

下面是我的尝试:

function q(elem){
var nodes = elem.querySelectorAll('someSeletor');
console.log(nodes);
for(var i = 0; i < nodes.length; i++){
if(nodes[i].parentNode === elem) return nodes[i];
}
}

http://jsfiddle.net/Lgaw5/8/

这对我很有效:

Node.prototype.search = function(selector)
{
if (selector.indexOf('@this') != -1)
{
if (!this.id)
this.id = "ID" + new Date().getTime();
while (selector.indexOf('@this') != -1)
selector = selector.replace('@this', '#' + this.id);
return document.querySelectorAll(selector);
} else
return this.querySelectorAll(selector);
};

当您想要搜索直接的孩子时,您必须在 > 之前传递@this keywork。

下面是一个简化的、通用的方法,用于仅在直接子级上运行任何 CSS 选择器查询——它还考虑了组合查询,比如 "foo[bar], baz.boo":

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;
}




*** Example Use ***


queryChildren(someElement, '.foo, .bar[xyz="123"]');

完成: 范围填充

因为 Avetisk提到选择器 API 2使用 :scope伪选择器。
为了在所有支持 querySelector的浏览器中实现这一点,这里使用了 polyfill

(function(doc, proto) {
try { // check if browser supports :scope natively
doc.querySelector(':scope body');
} catch (err) { // polyfill native methods if it doesn't
['querySelector', 'querySelectorAll'].forEach(function(method) {
var nativ = proto[method];
proto[method] = function(selectors) {
if (/(^|,)\s*:scope/.test(selectors)) { // only if selectors contains :scope
var id = this.id; // remember current element id
this.id = 'ID_' + Date.now(); // assign new unique id
selectors = selectors.replace(/((^|,)\s*):scope/g, '$1#' + this.id); // replace :scope with #ID
var result = doc[method](selectors);
this.id = id; // restore previous id
return result;
} else {
return nativ.call(this, selectors); // use native code for other selectors
}
}
});
}
})(window.document, Element.prototype);

用法

node.querySelector(':scope > someselector');
node.querySelectorAll(':scope > someselector');

由于历史原因,我之前的解决方案

基于所有的答案

// Caution! Prototype extending
Node.prototype.find = function(selector) {
if (/(^\s*|,\s*)>/.test(selector)) {
if (!this.id) {
this.id = 'ID_' + new Date().getTime();
var removeId = true;
}
selector = selector.replace(/(^\s*|,\s*)>/g, '$1#' + this.id + ' >');
var result = document.querySelectorAll(selector);
if (removeId) {
this.id = null;
}
return result;
} else {
return this.querySelectorAll(selector);
}
};

用法

elem.find('> a');

虽然这并不是一个完整的答案,但是你应该关注一下 W3C 选择器 API v. 2,它已经在大多数浏览器中可用了,包括桌面和移动设备,除了 IE (Edge 似乎支持) : 请参阅完全支持清单

function(elem) {
return elem.querySelectorAll(':scope > someselector');
};

有一个 查询关系库,这是相当方便的替代 查询选择器。它填充子选择器 '> *':scope(inc. 。IE8) ,以及规范化 :root选择器。 它还提供了一些特殊的相对伪码,如 :closest:parent:prev:next,以防万一。

检查元素是否有标识符或者添加随机标识符并根据它进行搜索

function(elem) {
if(!elem.id)
elem.id = Math.random().toString(36).substr(2, 10);
return elem.querySelectorAll(elem.id + ' > someselector');
};

会做同样的事情

$("> someselector",$(elem))

如果您知道正在查找的元素的标记名称,那么您可以在选择器中使用它来实现您想要的结果。

例如,如果你有一个包含 <option><optgroups><select>,你只想要它的直接子级 <option>,而不是 <optgoups>中的子级:

<select>
<option>iPhone</option>
<optgroup>
<option>Nokia</option>
<option>Blackberry</option>
</optgroup>
</select>

因此,通过对 select 元素的引用,您可以ーー令人惊讶地ーー得到它的直接子元素,如下所示:

selectElement.querySelectorAll('select > option')

它似乎可以在 Chrome、 Safari 和 Firefox 中运行,但是没有在 IE 中进行测试。 =/

如果你想最终找到直接的孩子(而不是例如 > div > span) ,你可以试试 匹配():

const elem = document.body
const elems = [...elem.children].filter(e => e.matches('b'))


console.log(elems)
<a>1</a>
<b>2</b>
<b>3</b>
<b>4</b>
<s>5</s>