querySelector搜索直接子级


96

我有一些类似jQuery的功能:

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

问题是我该怎么做querySelector()

问题在于>选择器中querySelector()要求显式指定父级。有什么解决方法吗?


我添加了一个可能对您更好的答案,让我知道;)
csuwldcat

Answers:


119

尽管这不是一个完整的答案,但您应关注W3C Selector API v.2,该版本已在大多数浏览器(包括台式机和移动设备)中提供,但IE(Edge似乎支持)除外:请参阅完整的支持列表

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

12
这是正确的答案。但是,浏览器支持有限,如果要使用它,则需要使用垫片。我为此构建了scopedQuerySelectorShim
lazd 2014年

3
:scope属性已从当前规范中删除
tobi

6
实际上,:scope仍然在drafts.c​​sswg.org/selectors-4/#the-scope-pseudo中指定了。到目前为止,仅<style scoped>删除了该属性。目前尚不清楚这是否还会导致:scope被删除(因为这<style scoped>是的重要用例:scope)。
约翰·梅勒

1
令人惊讶的是:只有Edge不支持
:)

31

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

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的源代码


1
在撰写本文时是正确的,但请参阅@avetisk的答案以获取更新的方法。
lazd 2014年

23

完成:范围polyfill

正如avetisk提到的, Selectors 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()
Christophe

4

如果知道要查找的元素的标签名称,则可以在选择器中使用它来实现所需的功能。

例如,如果您有一个<select>具有,<option>和的<optgroups>,并且只希望<option>s是其直接子代,而不是其内部的子代<optgoups>

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

因此,令人惊讶的是,通过引用select元素,您可以像下面这样获得它的直接子元素:

selectElement.querySelectorAll('select > option')

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


8
警告:这个答案实际上并不是限制范围。如果在更深的嵌套级别重复执行模式,即使它不是直接子代,也仍会选择该模式。 <ul id="start"> <li>A</li> <li> <ul> <li>b</li> <li>c</li> </ul> </li> </ul> var result = document.getElementById('start').querySelectorAll('ul>li'); ->将选择所有4个li元素!
Risord

1
上帝禁止<select>同一个父母有两个要素。
托马什Zato -恢复莫妮卡

4

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

const elems = Array.from(elem.children).filter(e => e.matches('.my-class'))

2

要求

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


1
几个问题。@disfated希望它与一起使用querySelector,这意味着:eq()无法使用。即使可以,选择器也将返回在:eq()页面上显示的元素,而不是:eq()其父元素的索引(在该位置获取idx)。
user113716 2011年

+1:eq()和querySelector。我应该添加上下文$(elem).parent()。
Liangliang Zheng

不幸的是,这也不起作用。当选择器从父级上下文运行时,它从最左边的子级开始,并找到所有匹配的元素,无论嵌套的深度如何,继续。假设elem在索引4处是,但之前的兄弟姐妹具有tagName,但与之不同,但是它在其中嵌套了一个具有match的元素tagName,该嵌套元素将在结果中包含在您要定位的元素之前,再次抛出索引。这是一个示例
user113716 2011年

...无论如何,您最终要做的是问题中代码的更复杂版本,该版本确实有效。$('> someselector', elem);; o)
user113716 2011年

是的,但是您需要对单个增量进行硬编码。那将需要先验知识。如果添加另一个类似的元素,它将再次损坏。; o)jsfiddle.net/Lgaw5/3
user113716 2011年

1

这对我有用:

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键盘输入。


似乎与当前接受的答案相同...顺便说一句,奇怪的“ @this”语法有什么意义?为什么不只用regexp测试“>”?
2013年

1

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

1

有一个相对于查询的库,可以很方便地替换query-selector。它会填充子选择器'> *':scope(包括IE8),并归一化:root选择器。此外,它提供了像一些特殊的相对假点:closest:parent:prev:next,以防万一。


0

检查元素是否具有ID,否则添加随机ID并根据其进行搜索

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

将做与

$("> someselector",$(elem))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.