querySelector tìm kiếm trẻ em ngay lập tức


95

Tôi có một số chức năng giống như jquery:

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

Câu hỏi là làm thế nào tôi có thể làm điều tương tự với querySelector()?

Vấn đề là >bộ chọn trong querySelector()yêu cầu cha mẹ phải được chỉ định rõ ràng. Có bất kỳ công việc xung quanh?


Tôi nói thêm một câu trả lời có thể làm việc tốt hơn cho bạn, cho tôi biết;)
csuwldcat

Câu trả lời:


118

Mặc dù đây không phải là câu trả lời đầy đủ, nhưng bạn nên theo dõi API W3C Selector v.2 đã có sẵn trong hầu hết các trình duyệt, cả máy tính để bàn và thiết bị di động, ngoại trừ IE (Edge có vẻ hỗ trợ): xem danh sách hỗ trợ đầy đủ .

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

12
Đây là câu trả lời chính xác. Tuy nhiên, hỗ trợ trình duyệt bị hạn chế và bạn sẽ cần một miếng đệm nếu muốn sử dụng. Tôi đã xây dựng scopedQuerySelectorShim cho mục đích này.
lazd


6
Trên thực tế, :scopevẫn được chỉ định tại drafts.csswg.org/selectors-4/#the-scope-pseudo . Cho đến nay, nó chỉ là <style scoped>thuộc tính bị loại bỏ; không rõ liệu điều đó cũng sẽ dẫn đến :scopeviệc bị xóa (vì <style scoped>là một trường hợp sử dụng quan trọng cho :scope).
John Mellor

1
Thật là một sự ngạc nhiên: chỉ cạnh không hỗ trợ này:)
Xenos

31

Bạn không thể. Không có bộ chọn nào mô phỏng điểm bắt đầu của bạn.

Cách jQuery làm điều đó (nhiều hơn do cách qsacư xử không theo ý họ), đó là họ kiểm tra xem elemcó ID hay không, và nếu không, họ tạm thời thêm ID, sau đó tạo một chuỗi bộ chọn đầy đủ.

Về cơ bản bạn sẽ làm:

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

Đây chắc chắn không phải là mã jQuery, nhưng theo những gì tôi nhớ, về cơ bản nó là những gì họ làm. Không chỉ trong tình huống này, mà trong bất kỳ tình huống nào bạn đang chạy một bộ chọn từ ngữ cảnh của một phần tử lồng nhau.

Mã nguồn cho Sizzle


1
Đúng tại thời điểm viết bài, nhưng hãy xem câu trả lời của @ avetisk để biết phương pháp cập nhật.
lazd

23

Hoàn thành: polyfill phạm vi

Như avetisk đã đề cập Bộ chọn API 2 sử dụng :scopebộ chọn giả.
Để làm cho điều này hoạt động trong tất cả các trình duyệt (hỗ trợ querySelector), đây là 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);

Sử dụng

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

Vì lý do lịch sử, giải pháp trước đây của tôi

Dựa trên tất cả các câu trả lời

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

Sử dụng

elem.find('> a');

Date.now()không được hỗ trợ bởi các trình duyệt cũ, và có thể được thay thế bằngnew Date().getTime()
Christophe

4

Nếu bạn biết tên thẻ của phần tử bạn đang xem xét, thì bạn có thể sử dụng nó trong bộ chọn để đạt được những gì bạn muốn.

Ví dụ: nếu bạn có một <select>cái có <option>s và <optgroups>và bạn chỉ muốn những <option>cái là con của nó, không phải những cái bên trong <optgoups>:

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

Vì vậy, khi có tham chiếu đến phần tử select, bạn có thể - đáng ngạc nhiên là - có được phần tử con ngay lập tức của nó như thế này:

selectElement.querySelectorAll('select > option')

Nó dường như hoạt động trong Chrome, Safari và Firefox, nhưng không thử nghiệm trong IEs. = /


8
Cảnh báo: Câu trả lời này không thực sự giới hạn phạm vi. Nếu mẫu lặp lại ở mức tổ sâu hơn, nó vẫn sẽ được chọn ngay cả khi nó không phải là con trực tiếp. <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'); -> Tất cả 4 phần tử li sẽ được chọn!
Risord

1
Chúa cấm có hai <select>yếu tố trong cùng một cha mẹ.
Tomáš Zato - Phục hồi Monica

4

Nếu cuối cùng bạn muốn tìm các con trực tiếp (chứ không phải ví dụ > div > span), bạn có thể thử Element.matches () :

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

2

YÊU CẦU

Cá nhân tôi sẽ lấy câu trả lời từ Patrick dw và +1 câu trả lời của anh ấy, câu trả lời của tôi là để tìm kiếm giải pháp thay thế. Tôi không nghĩ rằng nó xứng đáng nhận được sự ủng hộ.

Đây là nỗ lực của tôi:

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

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


1
Một số vấn đề với điều đó. @disfated muốn nó hoạt động querySelector, có nghĩa là :eq()không thể sử dụng được. Ngay cả khi nó có thể, bộ chọn của bạn sẽ trả về phần tử giống :eq()như sự xuất hiện của nó trên trang, chứ không :eq()phải chỉ mục của phần tử gốc của nó (chính là nơi bạn nhận được idx).
dùng113716

+1 về: eq () & querySelector. Và tôi nên thêm ngữ cảnh $ (elem) .parent ().
Liangliang Zheng

Thật không may, điều đó cũng sẽ không hoạt động. Khi bộ chọn chạy từ ngữ cảnh gốc, nó bắt đầu với phần tử con bên trái nhất và tìm thấy tất cả các phần tử phù hợp bất kể được lồng sâu đến mức nào, vẫn tiếp tục. Vì vậy, giả sử elemlà ở chỉ mục 4, nhưng có một người anh em trước đó có chỉ mục khác tagName, nhưng nó đã lồng vào bên trong nó một phần tử có kết quả phù hợp tagName, phần tử lồng nhau đó sẽ được đưa vào kết quả trước phần tử bạn đang nhắm mục tiêu, một lần nữa bị loại bỏ chỉ số. Đây là một ví dụ
user113716

... dù sao đi nữa, những gì bạn đang làm cuối cùng là một phiên bản phức tạp hơn của mã trong câu hỏi, nó hoạt động. $('> someselector', elem);; o)
dùng113716

Có nhưng bạn cần phải viết mã số gia tăng duy nhất đó. Điều đó sẽ đòi hỏi kiến ​​thức trước về nó. Nếu tôi thêm một yếu tố tương tự khác, nó lại bị hỏng. ; o) jsfiddle.net/Lgaw5/3
user113716

1

Điều đó đã làm việc cho tôi:

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

bạn sẽ phải vượt qua keywork @this trước> khi bạn muốn tìm kiếm những đứa trẻ ngay lập tức.


Có vẻ giống với câu trả lời được chấp nhận hiện tại ... Btw, cú pháp "@this" kỳ lạ ở điểm nào? Tại sao không chỉ kiểm tra ">" với regexp?
disfated

1

Sau đây là một phương pháp đơn giản, chung chung để chạy bất kỳ truy vấn bộ chọn CSS nào trên chỉ con trực tiếp - nó cũng tính đến các truy vấn kết hợp, như "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

Có một lib tương đối truy vấn , thay thế khá tiện dụng cho công cụ chọn truy vấn . Nó làm đầy bộ chọn trẻ em '> *':scope(bao gồm IE8), cũng như chuẩn hóa :rootbộ chọn. Ngoài ra nó cung cấp một số pseudos tương đối đặc biệt như :closest, :parent, :prev, :next, chỉ trong trường hợp.


0

kiểm tra xem phần tử có id hay không, hãy thêm id ngẫu nhiên và thực hiện tìm kiếm dựa trên nó

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

sẽ làm điều tương tự như

$("> someselector",$(elem))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.