querySelector ค้นหาเด็กทันที


102

ฉันมีฟังก์ชั่นเหมือน jquery:

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

คำถามคือฉันจะทำเช่นเดียวกันกับquerySelector()?

ปัญหาคือ>ตัวเลือกในquerySelector()ต้องระบุพาเรนต์อย่างชัดเจน มีวิธีแก้ปัญหาใด ๆ หรือไม่?


ฉันได้เพิ่มคำตอบที่อาจทำงานได้ดีขึ้นสำหรับคุณแจ้งให้เราทราบ;)
csuwldcat

คำตอบ:


127

แม้ว่ามันจะไม่ได้คำตอบที่เต็มคุณควรจับตามองในv.2 API W3C ตัวเลือกที่มีอยู่แล้วในเบราว์เซอร์มากที่สุดทั้งสก์ท็อปและโทรศัพท์มือถือยกเว้น IE (ขอบดูเหมือนว่าจะสนับสนุน): ดูรายการการสนับสนุนอย่างเต็มที่

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

13
นี่คือคำตอบที่ถูกต้อง อย่างไรก็ตามการรองรับเบราว์เซอร์มีข้อ จำกัดและคุณจะต้องใช้ shim หากต้องการใช้งาน ฉันสร้างscopedQuerySelectorShimเพื่อจุดประสงค์นี้
lazd

3
นี้แอตทริบิวต์ขอบเขตได้ถูกลบออกจากข้อกำหนดปัจจุบัน
tobi

7
ที่จริง:scopeยังคงระบุที่drafts.csswg.org/selectors-4/#the-scope-pseudo จนถึงตอนนี้เป็นเพียง<style scoped>แอตทริบิวต์ที่ถูกลบออกไป ไม่ชัดเจนว่าจะนำไปสู่การ:scopeถูกลบหรือไม่ (เนื่องจาก<style scoped>เป็นกรณีการใช้งานที่สำคัญสำหรับ:scope)
John Mellor

2
สิ่งที่น่าแปลกใจ: มีเพียง Edge เท่านั้นที่ไม่รองรับสิ่งนี้
Xenos

33

คุณทำไม่ได้ ไม่มีตัวเลือกที่จะจำลองจุดเริ่มต้นของคุณ

วิธีที่ 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

25

เสร็จสมบูรณ์: ขอบเขต 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

6

หากคุณต้องการค้นหาลูกโดยตรงในที่สุด (ไม่ใช่เช่น> 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/


1
คู่ของปัญหากับสิ่งนั้น @disfated ต้องการให้ใช้งานได้querySelectorซึ่งหมายความว่า:eq()ไม่สามารถใช้งานได้ แม้ว่าจะทำได้ แต่ตัวเลือกของคุณจะส่งคืนองค์ประกอบที่มี:eq()ลักษณะปรากฏบนหน้าเว็บไม่ใช่:eq()ไปที่ดัชนีของพาเรนต์ (ซึ่งเป็นที่ที่คุณได้รับidx)
user113716

+1 เกี่ยวกับ: eq () & querySelector และฉันควรเพิ่มบริบท $ (elem) .parent ()
Liangliang Zheng

น่าเสียดายที่ใช้ไม่ได้เช่นกัน เมื่อตัวเลือกทำงานจากบริบทของพาเรนต์จะเริ่มต้นด้วยซ้ายสุดลูกและค้นหาองค์ประกอบที่ตรงกันทั้งหมดไม่ว่าจะซ้อนกันลึกแค่ไหน สมมติว่าelemอยู่ที่ดัชนี 4 แต่มีพี่น้องก่อนหน้านี้ที่มีความแตกต่างกันtagNameแต่มีการซ้อนอยู่ภายในองค์ประกอบที่มีการจับคู่tagNameองค์ประกอบที่ซ้อนกันจะรวมอยู่ในผลลัพธ์ก่อนที่คุณจะกำหนดเป้าหมายอีกครั้ง ดัชนี นี่คือตัวอย่าง
user113716

... อย่างไรก็ตามสิ่งที่คุณทำในท้ายที่สุดคือโค้ดรุ่นที่ซับซ้อนกว่าในคำถามซึ่งใช้งานได้ $('> someselector', elem);; o)
user113716

ใช่ แต่คุณต้องการฮาร์ดโค้ดที่เพิ่มทีละครั้ง นั่นจะต้องมีความรู้มาก่อน ถ้าฉันเพิ่มองค์ประกอบอื่นที่คล้ายกันมันจะหักอีกครั้ง ; o) jsfiddle.net/Lgaw5/3
user113716

5

หากคุณทราบชื่อแท็กขององค์ประกอบที่คุณกำลังมองหาคุณสามารถใช้ในตัวเลือกเพื่อให้บรรลุสิ่งที่คุณต้องการ

ตัวอย่างเช่นถ้าคุณมี<select>ที่มี<option>และ<optgroups>และคุณต้องการเฉพาะ<option>ของที่เป็นลูกของมันทันที, ไม่ได้คนที่อยู่ภายใน<optgoups>:

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

ดังนั้นเมื่อมีการอ้างอิงถึงองค์ประกอบที่เลือกคุณจึงสามารถรับลูกได้ทันทีเช่นนี้:

selectElement.querySelectorAll('select > option')

ดูเหมือนว่าจะใช้งานได้ใน Chrome, Safari และ Firefox แต่ไม่ได้ทดสอบใน IE = /


9
คำเตือน: คำตอบนี้ไม่ได้ จำกัด ขอบเขตจริงๆ หากมีการทำซ้ำรูปแบบระดับรังที่ลึกกว่าระบบจะยังคงเลือกแม้ว่าจะไม่ใช่ลูกโดยตรงก็ตาม <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>องค์ประกอบในพ่อแม่เดียวกัน
Tomáš Zato - คืนสถานะ Monica

2

ต่อไปนี้เป็นง่ายวิธีการทั่วไปสำหรับการเรียกใช้แบบสอบถามเลือก 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"]');

2

มีความเป็นแบบสอบถามญาติ lib ซึ่งเป็นทดแทนที่มีประโยชน์มากสำหรับแบบสอบถามเลือก เป็นตัวเลือกเด็ก polyfills '> *'และ:scope(รวม IE8) รวมทั้ง:rootตัวเลือกปกติ นอกจากนี้ยังจะให้ pseudos ญาติบางพิเศษเช่น:closest, :parent, :prev, :nextเพียงในกรณีที่


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 keywork ก่อน> เมื่อคุณต้องการค้นหาเด็กในทันที


ดูเหมือนว่าคำตอบที่ยอมรับในปัจจุบัน... Btw ไวยากรณ์ "@this" แปลก ๆ คืออะไร? ลองทดสอบ ">" ด้วย regexp ดูล่ะ
เสียโฉม

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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.