Utilizzo di querySelectorAll per recuperare i bambini diretti

Sono in grado di farlo:

 myDiv = getElementById("myDiv"); myDiv.querySelectorAll("#myDiv > .foo"); 

Cioè, posso recuperare con successo tutti i figli diretti dell’elemento .foo che hanno class .foo .

Il problema è che mi infastidisce il fatto che devo includere #myDiv nel selettore, perché sto eseguendo la query sull’elemento myDiv (quindi è ovviamente ridondante).

Dovrei essere in grado di lasciare spento #myDiv , ma il selettore non è una syntax legale poiché inizia con un > .

Qualcuno sa come scrivere un selettore che ottiene solo i figli diretti dell’elemento su cui è in esecuzione il selettore?

Bella domanda, ma come vedi non c’è modo di fare “query combinatorie con root” come li chiama John Resig .

Tuttavia, in alcuni casi puoi semplicemente saltare .querySelectorAll e utilizzare altre buone funzionalità API DOM vecchio stile. Invece di myDiv.querySelectorAll("> *") non myDiv.children , puoi semplicemente scrivere myDiv.children , ad esempio.

Purtroppo non riesco a pensare a un modo per gestire la situazione senza aggiungere più logica di filtro personalizzata (ad esempio, trova myDiv.getElementsByClassName("foo") cui .parentNode === myDiv ), e ovviamente non è l’ideale se stai cercando di supportare un percorso di codice che vuole solo prendere una stringa selettrice arbitraria come input e una lista di corrispondenze come output! Ma se come me hai finito per fare questa domanda semplicemente perché ti sei bloccato pensando che “tutto ciò che avevi era un martello” non dimenticare che ci sono anche altri strumenti che il DOM offre.

Qualcuno sa come scrivere un selettore che ottiene solo i figli diretti dell’elemento su cui è in esecuzione il selettore?

Il modo corretto di scrivere un selettore che è “rooted” per l’elemento corrente è utilizzare :scope .

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

Tuttavia, il supporto del browser è limitato e avrai bisogno di uno shim se vuoi usarlo. Ho creato scopedQuerySelectorShim per questo scopo.

Nota : la pseudo-class :scope è stata deprecata. Tuttavia, continua a essere supportato in querySelector / querySelectorAll .

Ecco un metodo flessibile, scritto in vanilla JS, che consente di eseguire una query selector CSS solo sui figli diretti di un elemento:

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

se sai per certo che l’elemento è unico (come il tuo caso con l’ID):

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

Per una soluzione più “globale”: (usa uno spessore MatchSelector )

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

dove elm è il tuo elemento genitore e sel è il tuo selettore. Potrebbe essere completamente utilizzato anche come prototipo.

Ho creato una funzione per gestire questa situazione, ho pensato di condividerla.

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

In sostanza quello che stai facendo è generare una stringa casuale (la funzione randomString qui è un modulo importato di npm, ma puoi crearne una tua.) Quindi usare quella stringa casuale per garantire di ottenere l’elemento che ti aspetti nel selettore. Quindi sei libero di usare il > dopo.

La ragione per cui non sto usando l’attributo id è che l’attributo id potrebbe essere già usato e non voglio sovrascriverlo.

Bene possiamo facilmente ottenere tutti i figli diretti di un elemento usando childNodes e possiamo selezionare antenati con una class specifica con querySelectorAll , quindi non è difficile immaginare che potremmo creare una nuova funzione che ottiene entrambi e confronta i due.

 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 

Nota: questo restituirà una matrice di nodes, non una lista dei nodes.

uso

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

La seguente soluzione è diversa da quelle proposte finora e funziona per me.

La logica è che prima selezioni tutti i bambini corrispondenti e poi filtri quelli che non sono figli diretti. Un figlio è un figlio diretto se non ha un genitore corrispondente con lo stesso selettore.

 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!

Vorrei aggiungere che è ansible estendere la compatibilità di : scope assegnando solo un attributo temporaneo al nodo corrente.

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

Lo sto facendo solo senza nemmeno provarlo. Funzionerebbe?

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

Fare un tentativo, forse funziona forse no. Apolovies, ma non sono su un computer ora per provarlo (rispondendo dal mio iPhone).

Sarei andato con

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