querySelector buscar hijos inmediatos


96

Tengo una función similar a jquery:

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

La pregunta es ¿cómo puedo hacer lo mismo con querySelector()?

El problema es que el >selector querySelector()requiere que el padre se especifique explícitamente. ¿Hay algún trabajo alrededor?


Respuestas:


119

Aunque no es una respuesta completa, debe estar atento a la API de selector de W3C v.2, que ya está disponible en la mayoría de los navegadores, tanto de escritorio como móviles, excepto IE (Edge parece ser compatible): consulte la lista de compatibilidad completa .

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

12
Esta es la respuesta correcta. Sin embargo, la compatibilidad con el navegador es limitada y necesitará una cuña si desea utilizarla. Construí scopedQuerySelectorShim para este propósito.
lazd


6
En realidad, :scopetodavía se especifica en drafts.csswg.org/selectors-4/#the-scope-pseudo . Hasta ahora, solo <style scoped>se eliminó el atributo; no está claro si eso también conducirá a :scopesu eliminación (ya que <style scoped>fue un caso de uso importante para :scope).
John Mellor

1
Qué sorpresa: solo Edge no admite esto
:)

31

No puedes. No hay ningún selector que simule su punto de partida.

La forma en que jQuery lo hace (más por una forma que se qsacomporta que no es de su agrado), es que verifican si elemtiene una ID, y si no, agregan temporalmente una ID, luego crean una cadena de selección completa.

Básicamente harías:

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

Esto ciertamente no es código jQuery, pero por lo que recuerdo, es básicamente lo que hacen. No solo en esta situación, sino en cualquier situación en la que esté ejecutando un selector desde el contexto de un elemento anidado.

Código fuente de Sizzle


1
Correcto en el momento de escribir este artículo, pero consulte la respuesta de @ avetisk para obtener un método actualizado.
lazd

23

Completo: polyfill de alcance

Como ha mencionado avetisk, Selectors API 2 usa pseudo-selector. Para que esto funcione en todos los navegadores (que admiten ) aquí está el 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);

Uso

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

Por razones históricas, mi solución anterior

Basado en todas las respuestas

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

Uso

elem.find('> a');

Date.now()no es compatible con navegadores más antiguos y podría reemplazarse connew Date().getTime()
Christophe

4

Si conoce el nombre de etiqueta del elemento que está investigando, puede usarlo en el selector para lograr lo que desea.

Por ejemplo, si tiene un <select>que tiene <option>s y <optgroups>, y solo desea los <option>s que son sus hijos inmediatos, no los que están dentro <optgoups>:

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

Entonces, teniendo una referencia al elemento seleccionado, puede, sorprendentemente, obtener sus hijos inmediatos de esta manera:

selectElement.querySelectorAll('select > option')

Parece funcionar en Chrome, Safari y Firefox, pero no se probó en IEs. = /


8
Advertencia: esta respuesta no limita realmente el alcance. Si el patrón se repite en un nivel de nido más profundo, aún se seleccionará incluso si no son hijos directos. <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'); -> ¡Se seleccionarán los 4 elementos li!
Risord

1
Dios no quiera que haya dos <select>elementos en el mismo padre.
Tomáš Zato - Reincorporación a Monica

4

Si finalmente desea encontrar hijos directos (y no, por ejemplo > div > span), puede probar Element.matches () :

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

2

RECLAMACIÓN

Personalmente, tomaría la respuesta de patrick dw y +1 en su respuesta, mi respuesta es buscar una solución alternativa. No creo que merezca un voto negativo.

Aquí está mi intento:

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

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


1
Un par de problemas con eso. @disfated quiere que funcione querySelector, lo que significa que :eq()no se puede usar. Incluso si pudiera, su selector devolvería el elemento que tiene :eq()su apariencia en la página, no :eq()al índice de su padre (que es donde está obteniendo idx).
user113716

+1 sobre: ​​eq () & querySelector. Y debería agregar el contexto $ (elem) .parent ().
Liangliang Zheng

Desafortunadamente, eso tampoco funcionará. Cuando el selector se ejecuta desde el contexto del padre, comienza con el hijo más a la izquierda y encuentra todos los elementos coincidentes sin importar cuán profundamente anidados, continúa. Entonces, digamos que elemestá en el índice 4, pero hay un hermano anterior que tiene un elemento diferente tagName, pero ha anidado dentro de él un elemento con la coincidencia tagName, ese elemento anidado se incluirá en los resultados antes del que está apuntando, nuevamente arrojando El índice. Aquí hay un ejemplo
user113716

... de todos modos, lo que está haciendo en última instancia es una versión más compleja del código en la pregunta, que funciona. $('> someselector', elem);; o)
user113716

Sí, pero necesitaba codificar ese único incremento. Eso requeriría un conocimiento previo del mismo. Si agrego otro elemento similar, se rompe nuevamente. ; o) jsfiddle.net/Lgaw5/3
user113716

1

Eso funcionó para mí:

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

tendrá que pasar el @this keywork antes del> cuando desee buscar hijos inmediatos.


Parece lo mismo que la respuesta aceptada actualmente ... Por cierto, ¿cuál es el punto de la extraña sintaxis "@ esta"? ¿Por qué no probar ">" con expresiones regulares?
desapareció el

1

El siguiente es un método genérico simplificado para ejecutar cualquier consulta de selector de CSS solo sobre hijos directos; también tiene en cuenta las consultas combinadas, como "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

Hay una biblioteca relativa a consultas , que es un reemplazo bastante útil para el selector de consultas . Rellena el selector de niños '> *'y :scope(inc. IE8), así como normaliza el :rootselector. También ofrece algunos pseudos relativos especiales como :closest, :parent, :prev, :next, por si acaso.


0

verifique si el elemento tiene id; de lo contrario, agregue una identificación aleatoria y busque en función de ella

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

Hará lo mismo que

$("> someselector",$(elem))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.