Doğrudan alt öğeleri almak için querySelectorAll kullanma


119

Bunu yapabilirim:

<div id="myDiv">
   <div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");

Yani, myDivsınıfı olan öğenin tüm doğrudan alt öğelerini başarılı bir şekilde alabilirim .foo.

Sorun şu ki #myDiv, seçiciye şunu eklemem gerekmesi beni rahatsız ediyor , çünkü sorguyu myDivöğe üzerinde çalıştırıyorum (bu yüzden açıkça gereksizdir).

Kapalıyı bırakmam gerekir #myDiv, ancak o zaman seçici a ile başladığı için legal sözdizimi değildir >.

Seçicinin üzerinde çalıştığı öğenin doğrudan çocuklarını alan bir seçici yazmayı bilen var mı?



Cevaplardan hiçbiri ihtiyaç duyduğunuz şeyi başaramadı mı? Lütfen geri bildirim sağlayın veya bir yanıt seçin.
Randy Hall

@mattsh - İhtiyacınız için daha iyi (IMO) bir cevap ekledim, kontrol edin!
csuwldcat

Yanıtlar:


85

İyi soru. Sorulduğunda, "birleştirici köklü sorgular" yapmanın evrensel olarak uygulanan bir yolu ( John Resig'in dediği gibi ) mevcut değildi.

Şimdi : kapsam sözde sınıfı tanıtıldı. O değil desteklenen Kenardan veya IE'nin [öncesi Chrominum] sürümlerinde, ama zaten birkaç yıldır Safari tarafından desteklenmiştir. Bunu kullanarak kodunuz şöyle olabilir:

let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");

Bazı durumlarda .querySelectorAlldiğer eski moda DOM API özelliklerini de atlayıp kullanabileceğinizi unutmayın. Örneğin, myDiv.querySelectorAll(":scope > *")sizin yerinize myDiv.childrenörneğin yazabilirsiniz .

Henüz güvenemez Aksi takdirde :scope, ben (örn bulmak daha özel filtre mantığı eklemeden durumun üstesinden gelmek için başka bir yol düşünemiyorum myDiv.getElementsByClassName("foo")kimin .parentNode === myDivo gerçekten bir kod yolu desteklemeye çalışıyorsanız), ve açıkçası değil idealdir sadece girdi olarak keyfi bir seçici dizge ve çıktı olarak eşleşmelerin bir listesini almak istiyor! Ama benim gibi bu soruyu sadece "sahip olduğun tek şey bir çekiç" diye takılıp kaldığın için sormaya başladıysan, DOM'un sunduğu çeşitli başka araçlar da olduğunu unutma .


3
myDiv.getElementsByClassName("foo")Aynı şey değil, myDiv.querySelectorAll("> .foo")daha çok myDiv.querySelectorAll(".foo")(aslında işe yarıyor) .foosadece çocuklar yerine tüm soyundan gelenleri bulması gibi.
BoltClock

Oh, zahmet et! Demek istediğim: kesinlikle haklısın. OP'nin durumunda pek iyi olmadığını daha net hale getirmek için cevabımı güncelledim.
natevw

bağlantı için teşekkürler (bu, ona kesin bir şekilde cevap veriyor gibi görünüyor) ve "birleştirici köklü sorgular" terminolojisini eklediğiniz için teşekkürler.
mattsh

1
John Resig'in "birleştirici-köklü sorgular" dediği şey, Seçiciler 4 şimdi bunları göreli seçiciler olarak adlandırıyor .
BoltClock

@Andrew Kapsamlı stil sayfalarının (yani <style scoped>) :scopebu yanıtta açıklanan sözde seçici ile hiçbir ilgisi yoktur .
natevw

124

Seçicinin üzerinde çalıştığı öğenin doğrudan çocuklarını alan bir seçici yazmayı bilen var mı?

Geçerli öğeye "köklenmiş" bir seçici yazmanın doğru yolu kullanmaktır :scope.

var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");

Ancak, tarayıcı desteği sınırlıdır ve kullanmak istiyorsanız bir şime ihtiyacınız olacaktır. Ben inşa scopedQuerySelectorShim bu amaçla.


3
:scopeSpesifikasyonun şu anda bir "Çalışma Taslağı" olduğunu ve bu nedenle değişebileceğini belirtmek gerekir . Kabul edildiğinde / kabul edildiğinde muhtemelen bu şekilde çalışacaktır, ancak bana göre bunu yapmanın "doğru yolu" olduğunu söylemek için biraz erken.
Zach Lysobey

2
Bu arada kullanımdan kaldırılmış gibi görünüyor
Adrian Moisa

1
3 yıl sonra ve gluteus maximusumu kurtardın!
Mehrad

5
@Adrian Moisa:: kapsam hiçbir yerde kullanımdan kaldırılmadı. Kapsamlı özellik ile karıştırıyor olabilirsiniz.
BoltClock

6

İşte, vanilya JS ile yazılmış, bir öğenin yalnızca doğrudan alt öğeleri üzerinde CSS seçici sorgusu çalıştırmanıza olanak tanıyan esnek bir yöntem:

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

Oluşturduğunuz kimliklere bir tür zaman damgası veya sayaç eklemenizi öneririm. Math.random().toString(36).substr(2, 10)Aynı jetonu birden fazla kez üretmek için sıfır olmayan (küçük de olsa) bir olasılık vardır .
Frédéric Hamidi

Yinelenen hash oranlarının çok küçük olması ihtimalinin ne kadar yüksek olduğundan emin değilim, kimlik yalnızca bir milisaniyenin bir kısmı için geçici olarak uygulanır ve herhangi bir dupe aynı ana düğümün alt öğesi de olmalıdır - temelde, olasılıklar astronomiktir. Ne olursa olsun, bir sayaç eklemek iyi görünüyor.
csuwldcat

Ne demek istediğinizden emin değilim any dupe would need to also be a child of the same parent node, idöznitelikler belge genelindedir. Haklısın, ihtimaller hala oldukça önemsiz, ama yüksek yolu seçip bu sayacı eklediğin için teşekkürler :)
Frédéric Hamidi

8
JavaScript paralel olarak çalıştırılmaz, bu nedenle şans aslında sıfırdır.
WGH

1
Vay @WGH, ben kesinlikle böyle basit bir noktada beyin osuruk olur hayrete, inanamıyorum (Mozilla azından ben iş ve daha uyu lazım, genellikle bu gerçeği okusun LOL!)
csuwldcat

5

Öğenin benzersiz olduğundan eminseniz (kimliğe sahip durumunuz gibi):

myDiv.parentElement.querySelectorAll("#myDiv > .foo");

Daha "global" bir çözüm için: (bir matchSelector şimi kullanın )

function getDirectChildren(elm, sel){
    var ret = [], i = 0, l = elm.childNodes.length;
    for (var i; i < l; ++i){
        if (elm.childNodes[i].matchesSelector(sel)){
            ret.push(elm.childNodes[i]);
        }
    }
    return ret;
}

elmAna öğeniz nerede ve selsizin seçiciniz. Tamamen bir prototip olarak da kullanılabilir.


@lazd bu sorunun bir parçası değil.
Randy Hall

Cevabınız "küresel" bir çözüm değil. Dikkat edilmesi gereken belirli sınırlamaları vardır, dolayısıyla benim yorumum.
lazd

sorulan soruyu cevaplayan @lazd. Öyleyse neden olumsuz oy? Yoksa senelik cevaba yapılan olumsuz oylamayı birkaç saniye içinde mi yorumladın?
Randy Hall

Çözüm matchesSelectoröneksiz kullanır (Chrome'un en son sürümünde bile çalışmaz), genel ad alanını kirletir (ret bildirilmemiştir), querySelectorMethods'un beklendiği gibi bir NodeList döndürmez. Bunun iyi bir çözüm olduğunu düşünmüyorum, dolayısıyla olumsuz oy.
lazd

@lazd - Orijinal soru öneksiz işlev ve genel ad alanı kullanıyor. Verilen soru için mükemmel bir çözüm. Bunun iyi bir çözüm olduğunu düşünmüyorsanız, kullanmayın veya bir düzenleme önermeyin. İşlevsel olarak yanlışsa veya spam ise, olumsuz oy kullanmayı düşünün.
Randy Hall

2

Aşağıdaki çözüm şimdiye kadar önerilenlerden farklı ve benim için çalışıyor.

Gerekçe, önce eşleşen tüm çocukları seçmeniz ve ardından doğrudan çocuk olmayanları filtrelemenizdir. Bir çocuk, aynı seçiciye sahip eşleşen bir ebeveyne sahip değilse, doğrudan çocuktur.

function queryDirectChildren(parent, selector) {
    const nodes = parent.querySelectorAll(selector);
    const filteredNodes = [].slice.call(nodes).filter(n => 
        n.parentNode.closest(selector) === parent.closest(selector)
    );
    return filteredNodes;
}

HTH!


Bunu kısalığı ve yaklaşımın basitliği için seviyorum, ancak büyük ağaçlarla ve aşırı açgözlü seçicilerle yüksek frekansta çağrılırsa büyük olasılıkla iyi sonuç vermeyecektir.
brennanyoung

1

Bu durumu ele almak için bir fonksiyon yarattım, paylaşacağımı düşündüm.

getDirectDecendent(elem, selector, all){
    const tempID = randomString(10) //use your randomString function here.
    elem.dataset.tempid = tempID;

    let returnObj;
    if(all)
        returnObj = elem.parentElement.querySelectorAll(`[data-tempid="${tempID}"] > ${selector}`);
    else
        returnObj = elem.parentElement.querySelector(`[data-tempid="${tempID}"] > ${selector}`);

    elem.dataset.tempid = '';
    return returnObj;
}

Temelde yaptığınız şey bir rastgele dizge oluşturmaktır (burada randomString işlevi içe aktarılmış bir npm modülüdür, ancak kendinizinkini yapabilirsiniz.) Ve ardından seçicide beklediğiniz öğeyi elde etmenizi garantilemek için bu rastgele dizeyi kullanmaktır. O zaman bundan >sonrasını kullanmakta özgürsünüz .

İd özniteliğini kullanmamamın nedeni, id özniteliğinin zaten kullanılmış olması ve bunu geçersiz kılmak istemiyorum.


0

Bir öğenin tüm doğrudan çocuklarını kullanarak kolayca elde edebiliriz childNodesve belirli bir sınıfa sahip ataları seçebiliriz querySelectorAll, bu yüzden ikisini birden alıp ikisini karşılaştıran yeni bir işlev yaratabileceğimizi hayal etmek zor değil.

HTMLElement.prototype.queryDirectChildren = function(selector){
  var direct = [].slice.call(this.directNodes || []); // Cast to Array
  var queried = [].slice.call(this.querySelectorAll(selector) || []); // Cast to Array
  var both = [];
  // I choose to loop through the direct children because it is guaranteed to be smaller
  for(var i=0; i<direct.length; i++){
    if(queried.indexOf(direct[i])){
      both.push(direct[i]);
    }
  }
  return both;
}

Not: Bu bir NodeList değil, bir Node Dizisi döndürecektir.

kullanım

 document.getElementById("myDiv").queryDirectChildren(".foo");

0

Sadece geçerli düğüme geçici bir öznitelik atayarak : kapsamının uyumluluğunu genişletebileceğinizi eklemek isterim .

let node = [...];
let result;

node.setAttribute("foo", "");
result = window.document.querySelectorAll("[foo] > .bar");
// And, of course, you can also use other combinators.
result = window.document.querySelectorAll("[foo] + .bar");
result = window.document.querySelectorAll("[foo] ~ .bar");
node.removeAttribute("foo");

-1

İle gitmiştim

var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;

-2

Bunu denemeden bile yapıyorum. Bu işe yarar mı?

myDiv = getElementById("myDiv");
myDiv.querySelectorAll(this.id + " > .foo");

Bir deneyin, belki işe yarayabilir belki de. Apolovies, ama şu anda denemek için bilgisayar başında değilim (iPhone'umdan yanıt veriyor).


3
QSA, çağrıldığı öğenin kapsamını içerir, bu nedenle bu, myDiv içinde myDiv ile aynı kimliğe sahip başka bir öğeyi ve ardından .foo ile alt öğelerini arar. `` Document.querySelectorAll ('#' + myDiv.id + '> .foo');
muhtemelenup
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.