2010-09-09 11 views
51

soy capaz de hacer esto:querySelectorAll para recuperar hijos directos

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

Es decir, que se puede recuperar con éxito todos los hijos directos del elemento myDiv que tienen clase .foo.

El problema es que me molesta que deba incluir el #myDiv en el selector, porque estoy ejecutando la consulta en el elemento myDiv (por lo que obviamente es redundante).

Debería poder dejar el #myDiv apagado, pero luego el selector no es sintaxis legal ya que comienza con un >.

¿Alguien sabe cómo escribir un selector que obtiene solo los elementos directos del elemento en el que se está ejecutando el selector?

+5

la caja http://stackoverflow.com/questions/6481612/queryselector-search-immediate-children – Anurag

+0

hizo ninguna de las respuestas lograr lo que necesitaba? Por favor, envíe sus comentarios o seleccione una respuesta. –

+0

@mattsh - Agregué una respuesta mejor (IMO) para su necesidad, ¡compruébelo! – csuwldcat

Respuesta

25

Buena pregunta, pero como ve no hay forma de hacer "consultas con raíz del combinador" como John Resig names them.

Sin embargo, en algunos casos, puede omitir .querySelectorAll y utilizar otras características de DOM API anticuadas. En lugar del myDiv.querySelectorAll("> *") no compatible, puede escribir myDiv.children, por ejemplo.

Lamentablemente no puedo pensar en una forma de manejar su situación sin agregar más lógica de filtro personalizada (por ejemplo, find myDiv.getElementsByClassName("foo") cuyo .parentNode === myDiv), y obviamente no es ideal si está tratando de admitir una ruta de código que realmente solo quiere tomar una cadena de selector arbitrario como entrada y una lista de coincidencias como salida! Pero si, como yo, terminaste haciéndote esta pregunta simplemente porque te quedaste atascado pensando que "todo lo que tenías era un martillo", no olvides que hay una variedad de otras herramientas que DOM también ofrece.

+0

'myDiv.getElementsByClassName (" foo ")' no es lo mismo que 'myDiv.querySelectorAll ("> .foo ")', es más parecido a 'myDiv.querySelectorAll (". Foo ")' (que en realidad funciona por manera) en que encuentra todos los descendientes '.foo's en comparación con solo los niños. – BoltClock

+0

¡Oh, molesta! Con lo que quiero decir: tienes razón. He actualizado mi respuesta para dejar en claro que no es muy buena en el caso del OP. – natevw

+0

gracias por el enlace (que parece responderlo definitivamente), y gracias por agregar la terminología "consultas raíz del combinador". – mattsh

-1

Estoy haciendo esto sin siquiera intentarlo. ¿Esto funcionaría?

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

Pruébelo, quizás funcione tal vez no. Apolovies, pero ahora no estoy en una computadora para probarlo (respondiendo desde mi iPhone).

+2

QSA tiene el alcance del elemento al que se está llamando, por lo que este buscará otro elemento dentro de myDiv con la misma ID que myDiv, luego sus hijos con .foo. Podría hacer algo como 'document.querySelectorAll ('#' + myDiv.id + '> .foo'); – probablyup

2

si se sabe con certeza el elemento es único (como por ejemplo el caso de la identificación):

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

Para una solución más "global": (utilizar un matchesSelector shim)

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

donde elm es su elemento primario y sel es su selector. Podría ser totalmente utilizado como prototipo también.

+0

@lazd que no es parte de la pregunta. –

+0

Su respuesta no es una solución "global". Tiene limitaciones específicas que deben tenerse en cuenta, de ahí mi comentario. – lazd

+0

@lazd que responde la pregunta. Entonces, ¿por qué votar abajo?¿O acabas de comentar en cuestión de segundos después del voto atrasado sobre la respuesta de un año? –

4

Aquí es un método flexible, escrito en JS vainilla, que le permite ejecutar una consulta selector CSS únicamente sobre los hijos directos de 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; 
} 
+0

Sugiero que agregue algún tipo de indicación de fecha y hora o contador a los ID que genere. Existe una probabilidad distinta de cero (aunque pequeña) para 'Math.random(). ToString (36) .substr (2, 10)' para producir el mismo token más de una vez. –

+0

No estoy seguro de cuán probable es que las probabilidades de repetición de hash sean minúsculas, la ID solo se aplica temporalmente durante una fracción de milisegundo, y cualquier víctima debería ser también hijo del mismo nodo padre: básicamente, las posibilidades son astronómicas. En cualquier caso, agregar un contador parece estar bien. – csuwldcat

+0

No estoy seguro de lo que quiere decir con 'cualquier tonto debería ser también un hijo del mismo nodo padre', los atributos' id' son de todo el documento. Tienes razón, las probabilidades todavía son bastante insignificantes, pero gracias por tomar el camino correcto y agregar ese contador :) –

66

¿Alguien sabe cómo escribir un selector que obtiene solo los elementos directos del elemento en el que se está ejecutando el selector

La forma correcta de escribir un selector que está "arraigado" para el elemento actual es el uso de :scope.

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

Sin embargo,browser support is limited y se necesita un suplemento si desea utilizarlo. Construí scopedQuerySelectorShim para este propósito.

Nota: La pseudo-clase :scope ya no se utiliza. Sin embargo, continúa siendo compatible con querySelector/querySelectorAll.

+3

Vale la pena mencionar que la especificación ': scope' es actualmente un" Borrador de trabajo "y, por lo tanto, está sujeto a cambios. Es probable que funcione así si/cuando se adopta, pero un poco pronto para decir que es el" correcto " manera "de hacerlo en mi opinión. –

+1

Parece que ha quedado obsoleto mientras tanto –

1

Creé una función para manejar esta situación, pensé que la compartiría.

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

En esencia, lo que está haciendo es generar una cadena aleatoria (función randomString aquí es un módulo de NPM importado, pero usted puede hacer su propio.) Y luego usar esa cadena aleatoria para garantizar que se obtiene el elemento que están esperando en el selector. Entonces puede usar el > después de eso.

La razón por la que no estoy usando el atributo id es porque el atributo id ya puede usarse y no quiero anularlo.

-1

me he ido con

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

Bien podemos llegar fácilmente a todos los hijos directos de un elemento utilizando childNodes y podemos seleccionar antepasados ​​con una clase específica con querySelectorAll, así que no es difícil imaginar que podría crear una nueva función que obtenga ambas y las compare.

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

Nota: Esto devolverá una matriz de nodos, no un NodeList.

Uso

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

La siguiente solución es diferente a los propuestos hasta ahora, y que funciona para mí.

La razón es que primero selecciona todos los elementos que coinciden y luego filtra los que no son hijos directos. Un niño es un niño directo si no tiene un padre coincidente con el mismo selector.

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!

Cuestiones relacionadas