querySelector 搜索直系子级 [重复]

Posted

技术标签:

【中文标题】querySelector 搜索直系子级 [重复]【英文标题】:querySelector search immediate children [duplicate] 【发布时间】:2011-09-22 19:43:49 【问题描述】:

我有一些类似 jquery 的功能:

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

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

问题是querySelector() 中的> 选择器需要明确指定父级。有什么解决办法吗?

【问题讨论】:

【参考方案1】:

虽然这不是一个完整的答案,但您应该关注W3C Selector API v.2,它已经在大多数浏览器中可用,包括桌面和移动设备,除了 IE(Edge 似乎支持):see full support list。

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

【讨论】:

这是正确答案。但是,browser support is limited 如果您想使用它,您将需要一个 shim。为此,我构建了scopedQuerySelectorShim。 :scope 属性has been removed from the current specification. 其实:scope还是指定在drafts.csswg.org/selectors-4/#the-scope-pseudo。到目前为止,仅删除了 <style scoped> 属性;目前还不清楚这是否也会导致:scope 被删除(因为<style scoped>:scope 的一个重要用例)。 真是惊喜:只有 Edge 不支持这个:) 到 2021 年,现在所有现代浏览器都支持此功能。【参考方案2】:

你不能。没有选择器可以模拟您的起点。

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 代码,但据我所知,这基本上是他们所做的。不仅在这种情况下,而且在您从嵌套元素的上下文中运行选择器的任何情况下。

Source code for Sizzle

【讨论】:

在撰写本文时正确,但请参阅 @avetisk 的答案以获取更新的方法。【参考方案3】:

完成 :scope polyfill

由于 avetisk 具有 mentioned 选择器 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');

【讨论】:

Date.now() 不支持旧版浏览器,可以替换为new Date().getTime()【参考方案4】:

如果您想最终找到直系子代(而不是例如> div > span),您可以尝试Element.matches():

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>

【讨论】:

【参考方案5】:

索赔

我个人会从 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/

【讨论】:

这有几个问题。 @disfated 希望它与 querySelector 一起工作,这意味着不能使用 :eq()。即使可以,您的选择器也会将 :eq() 的元素返回到它在页面上的外观,而不是将 :eq() 返回到其父级的索引(这是您获得 idx 的位置)。 +1 关于 :eq() 和 querySelector。我应该添加上下文 $(elem).parent()。 不幸的是,这也行不通。 When the selector runs from the context of the parent, it starts with the left most child and finds all matching elements no matter how deeply nested, the continues on.所以说elem在索引4处,但是有一个先前的兄弟有一个不同的tagName,但它在其中嵌套了一个匹配tagName的元素,该嵌套元素将包含在结果之前一个你的目标,再次抛出索引。 Here's an example ...无论如何,您最终要做的是问题中代码的更复杂版本,它确实有效。 $('&gt; someselector', elem); ;o) 是的,但是您需要对单个增量进行硬编码。这需要事先了解它。如果我添加另一个类似的元素,它会再次损坏。 ;o) jsfiddle.net/Lgaw5/3【参考方案6】:

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

例如,如果您有一个具有&lt;option&gt;s 和&lt;optgroups&gt;&lt;select&gt;,并且您只想要作为其直接子代的&lt;option&gt;s,而不是&lt;optgoups&gt; 中的子代:

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

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

selectElement.querySelectorAll('select > option')

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

【讨论】:

警告:这个答案实际上并没有限制范围。如果模式在更深的嵌套级别重复,即使它不是直接子级,它仍然会被选中。 &lt;ul id="start"&gt; &lt;li&gt;A&lt;/li&gt; &lt;li&gt; &lt;ul&gt; &lt;li&gt;b&lt;/li&gt; &lt;li&gt;c&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; var result = document.getElementById('start').querySelectorAll('ul&gt;li'); -> 将选择所有 4 个 li 元素! 上帝禁止在同一个父级中有两个 &lt;select&gt; 元素。【参考方案7】:

以下是一种简化的通用方法,用于仅对直接子级运行任何 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"]');

【讨论】:

【参考方案8】:

有一个 query-relative 库,它是 query-selector 的非常方便的替代品。它填充子选择器'&gt; *':scope(包括IE8),并规范化:root 选择器。 它还提供了一些特殊的相对伪像:closest:parent:prev:next,以防万一。

【讨论】:

【参考方案9】:

这对我有用:

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 键。

【讨论】:

似乎与当前接受的answer 相同...顺便说一句,奇怪的“@this”语法有什么意义?为什么不用正则表达式测试“>”?【参考方案10】:

检查元素是否有 id 否则添加随机 id 并根据它进行搜索

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

会做和

一样的事情
$("> someselector",$(elem))

【讨论】:

以上是关于querySelector 搜索直系子级 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Google App Engine 祖先查询

“document.querySelector(...)为空”错误[重复]

使用document.querySelector()[重复]

使flex子级内联块[重复]

如何使用 querySelector() 获取具有重复 id 的 div?

将 XElement 的子级转换为字符串 [重复]