querySelector pesquisa filhos imediatos


96

Eu tenho alguma função semelhante ao jquery:

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

A questão é como posso fazer o mesmo com querySelector()?

O problema é que o >seletor em querySelector()requer que o pai seja especificado explicitamente. Existe alguma solução alternativa?


Eu adicionei uma resposta que pode funcionar melhor para você, deixe-me saber;)
csuwldcat

Respostas:


119

Embora não seja uma resposta completa, você deve ficar de olho no W3C Selector API v.2, que já está disponível na maioria dos navegadores, tanto desktop quanto móvel, exceto IE (Edge parece suportar): veja a lista completa de suporte .

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

12
Essa é a resposta correta. No entanto, o suporte do navegador é limitado e você precisará de um calço se quiser usá-lo. Eu construí scopedQuerySelectorShim para esta finalidade.
tarde


6
Na verdade, :scopeainda está especificado em drafts.csswg.org/selectors-4/#the-scope-pseudo . Até agora, apenas o <style scoped>atributo foi removido; não está claro se isso também levará à :scoperemoção (já que <style scoped>era um caso de uso importante para :scope).
John Mellor de

1
Que surpresa: apenas o Edge não suporta isso
:)

31

Você não pode. Não há um seletor que simule seu ponto de partida.

A maneira como o jQuery faz isso (mais por causa de uma forma que qsanão é do seu agrado) é que eles verificam se elemtem um ID e, se não, eles temporariamente adicionam um ID e, em seguida, criam uma string de seletor completa.

Basicamente, você faria:

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

Certamente não é um código jQuery, mas pelo que me lembro, é basicamente o que eles fazem. Não apenas nesta situação, mas em qualquer situação em que você esteja executando um seletor a partir do contexto de um elemento aninhado.

Código fonte para Sizzle


1
Correto no momento da escrita, mas veja a resposta de @avetisk para um método atualizado.
tarde

23

Completo: escopo polyfill

Como avetisk foi mencionado selectores API 2 usos :scopepseudo-selector.
Para fazer isso funcionar em todos os navegadores (que suportam querySelector) aqui está o polyfill

(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 razões históricas, minha solução anterior

Com base em todas as respostas

// 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()não é suportado por navegadores mais antigos e pode ser substituído pornew Date().getTime()
Christophe

4

Se você souber o nome da tag do elemento que está procurando, poderá usá-lo no seletor para obter o que deseja.

Por exemplo, se você tem um <select>que tem <option>s e <optgroups>, e deseja apenas os <option>s que são seus filhos imediatos, não os internos <optgoups>:

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

Portanto, tendo uma referência ao elemento select, você pode - surpreendentemente - obter seus filhos imediatos assim:

selectElement.querySelectorAll('select > option')

Parece funcionar no Chrome, Safari e Firefox, mas não testou em IEs. = /


8
Aviso: essa resposta não está limitando o escopo. Se o padrão for repetido em um nível de aninhamento mais profundo, ele ainda será selecionado mesmo se não for filho direto. <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'); -> Todos os 4 elementos li serão selecionados!
Risord

1
Deus me livre, havia dois <select>elementos no mesmo pai.
Tomáš Zato - Reintegrar Monica

4

Se você quiser eventualmente encontrar filhos diretos (e não por exemplo > div > span), você pode tentar Element.matches () :

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

2

AFIRMAÇÃO

Pessoalmente, eu pegaria a resposta de patrick dw e +1 sua resposta, minha resposta é para buscar uma solução alternativa. Não acho que mereça um voto negativo.

Aqui está minha tentativa:

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

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


1
Alguns problemas com isso. @disfated deseja que funcione querySelector, o que significa que :eq()não pode ser usado. Mesmo se pudesse, seu seletor retornaria o elemento que está :eq()com sua aparência na página, não :eq()para o índice de seu pai (que é de onde você está obtendo o idx).
user113716

1 sobre o: eq () & querySelector. E devo adicionar o contexto $ (elem) .parent ().
Liangliang Zheng

Infelizmente, isso também não funcionará. Quando o seletor é executado a partir do contexto do pai, ele começa com o filho mais à esquerda e encontra todos os elementos correspondentes, não importa o quão profundamente aninhados, ele continua. Então digamos que elemestá no índice 4, mas há um irmão anterior que tem um diferente tagName, mas aninhado dentro dele um elemento com a correspondência tagName, esse elemento aninhado será incluído nos resultados antes daquele que você está almejando, novamente descartando o índice. Aqui está um exemplo
user113716

... de qualquer forma, o que você está fazendo é uma versão mais complexa do código em questão, que funciona. $('> someselector', elem);; o)
user113716

Sim, mas você precisava codificar aquele único incremento. Isso exigiria conhecimento prévio do mesmo. Se eu adicionar outro elemento semelhante, ele será quebrado novamente. ; o) jsfiddle.net/Lgaw5/3
user113716

1

Isso funcionou para mim:

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

você terá que passar o @this keywork antes do> quando quiser pesquisar os filhos imediatos.


Parece o mesmo que a resposta atualmente aceita ... Btw, qual é o ponto da estranha sintaxe "@esta"? Por que não apenas testar ">" com regexp?
desfavorecido em

1

O seguinte é um método simplificado e genérico para executar qualquer consulta do seletor CSS apenas em filhos diretos - ele também leva em consideração 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

Existe uma biblioteca relativa à consulta , que é um substituto bastante útil para o seletor de consulta . Ele polyfills seletor de crianças '> *'e :scope(inc. IE8), bem como :rootseletor normaliza . Também fornece algumas pseudos relativos especiais, como :closest, :parent, :prev, :next, apenas no caso.


0

verifique se o elemento tem id, então adicione um id aleatório e faça uma pesquisa com base nele

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

fará a mesma coisa que

$("> someselector",$(elem))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.