querySelector cerca i figli immediati


96

Ho qualche funzione simile a jquery:

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

La domanda è: come posso fare lo stesso con querySelector()?

Il problema è che il >selettore in querySelector()richiede che il genitore sia specificato esplicitamente. C'è qualche soluzione alternativa?


Ho aggiunto una risposta che potrebbe funzionare meglio per te, fammi sapere;)
csuwldcat,

Risposte:


119

Anche se non è una risposta completa, dovresti tenere d'occhio l' API Selector W3C v.2 che è già disponibile nella maggior parte dei browser, sia desktop che mobili, tranne IE (Edge sembra supportare): vedi l'elenco completo del supporto .

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

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


6
In realtà, :scopeè ancora specificato su drafts.csswg.org/selectors-4/#the-scope-pseudo . Finora è <style scoped>stato rimosso solo l' attributo; non è chiaro se ciò porterà anche alla :scoperimozione (poiché <style scoped>era un caso d'uso importante per :scope).
John Mellor

1
Che sorpresa: solo Edge non lo supporta
:)

31

Non puoi. Non esiste un selettore che simuli il tuo punto di partenza.

Il modo in cui jQuery lo fa (più a causa di un modo in cui qsasi comporta che non è di loro gradimento), è che controllano per vedere se elemha un ID e, in caso contrario, aggiungono temporaneamente un ID, quindi creano una stringa di selettore completa.

Fondamentalmente faresti:

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

Questo certamente non è codice jQuery, ma da quello che ricordo, è fondamentalmente quello che fanno. Non solo in questa situazione, ma in qualsiasi situazione in cui stai eseguendo un selettore dal contesto di un elemento nidificato.

Codice sorgente per Sizzle


1
Correggere al momento della scrittura, ma vedere la risposta di @ avetisk per un metodo aggiornato.
lazd

23

Completo: scope polyfill

Come ha accennato avetisk, Selectors API 2 utilizza uno pseudo-selettore. Per farlo funzionare in tutti i browser (che supportano ) ecco il polyfill:scope
querySelector

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

Utilizzo

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

Per ragioni storiche, la mia soluzione precedente

Basato su tutte le risposte

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

Utilizzo

elem.find('> a');

Date.now()non è supportato dai browser meno recenti e potrebbe essere sostituito connew Date().getTime()
Christophe

4

Se conosci il nome del tag dell'elemento che stai esaminando, puoi usarlo nel selettore per ottenere ciò che desideri.

Ad esempio, se hai un <select>che ha <option>s e <optgroups>, e vuoi solo gli <option>s che sono i suoi figli immediati, non quelli all'interno <optgoups>:

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

Quindi, facendo riferimento all'elemento select, puoi - sorprendentemente - ottenere i suoi figli immediati in questo modo:

selectElement.querySelectorAll('select > option')

Sembra funzionare in Chrome, Safari e Firefox, ma non è stato testato in IEs. = /


8
Avvertenza: questa risposta non limita effettivamente l'ambito. Se il pattern viene ripetuto a un livello di nido più profondo, verrà comunque selezionato anche se non è figlio diretto. <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'); -> Verranno selezionati tutti e 4 gli elementi li!
Risord

1
Dio non voglia che ci fossero due <select>elementi nello stesso genitore.
Tomáš Zato - Ripristina Monica il

4

Se alla fine vuoi trovare figli diretti (e non eg > div > span), puoi provare Element.matches () :

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

2

RICHIESTA

Personalmente prenderei la risposta da patrick dw e farebbe +1 sulla sua risposta, la mia risposta è per cercare una soluzione alternativa. Non penso che meriti un voto negativo.

Ecco il mio tentativo:

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

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


1
Un paio di problemi con quello. @disfated vuole che funzioni querySelector, il che significa che :eq()non può essere utilizzato. Anche se potesse, il tuo selettore restituirebbe l'elemento che è :eq()al suo aspetto sulla pagina, non :eq()all'indice del suo genitore (che è dove stai ottenendo il idx).
user113716

+1 su: eq () e querySelector. E dovrei aggiungere il contesto $ (elem) .parent ().
Liangliang Zheng

Sfortunatamente neanche questo funzionerà. Quando il selettore viene eseguito dal contesto del genitore, inizia con il figlio più a sinistra e trova tutti gli elementi corrispondenti non importa quanto profondamente nidificati, il continua. Quindi supponiamo che elemsia all'indice 4, ma c'è un fratello precedente che ne ha uno diverso tagName, ma al suo interno ha nidificato un elemento con la corrispondenza tagName, quell'elemento nidificato verrà incluso nei risultati prima di quello che stai prendendo di mira, gettando di nuovo via l'indice. Ecco un esempio
user113716

... comunque, quello che stai facendo alla fine è una versione più complessa del codice nella domanda, che funziona. $('> someselector', elem);; o)
user113716

Sì, ma era necessario codificare in modo rigido quel singolo incremento. Ciò richiederebbe una conoscenza preliminare di esso. Se aggiungo un altro elemento simile, si rompe di nuovo. ; o) jsfiddle.net/Lgaw5/3
user113716

1

Ha funzionato per me:

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

dovrai passare il @questo lavoro chiave prima del> quando vuoi cercare figli immediati.


Sembra la stessa della risposta attualmente accettata ... A proposito, qual è il punto della strana sintassi "@questa"? Perché non provare solo per ">" con regexp?
sfondato il

1

Il seguente è un metodo generico e semplificato per eseguire qualsiasi query del selettore CSS solo su figli diretti - tiene conto anche di query combinate, come "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'è una libreria relativa alla query , che è un sostituto abbastanza utile per il selettore di query . Riempie il selettore dei bambini '> *'e :scope(incluso IE8), oltre a normalizzare il :rootselettore. Inoltre fornisce alcuni particolari pseudos relativi come :closest, :parent, :prev, :next, per ogni evenienza.


0

controlla se l'elemento ha un ID altrimenti aggiungi un ID casuale ed esegui la ricerca in base ad esso

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

farà la stessa cosa di

$("> someselector",$(elem))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.