Selecione todos os elementos com o atributo "data-" sem usar o jQuery

233

Usando apenas JavaScript, qual é a maneira mais eficiente de selecionar todos os elementos DOM que possuem um determinado data-atributo (digamos data-foo). Os elementos podem ser diferentes elementos de tag.

<p data-foo="0"></p><br/><h6 data-foo="1"></h6>
DrANoel
fonte
Lembre-se de que document.querySelectorAllnão funciona no IE7. Você teria que criar um script de fallback que percorresse a árvore DOM e verificasse o atributo em cada tag (na verdade, eu não tenho ideia de quão rápido querySelectorAllé, e iria para a verificação manual de tags).
tereško
Qual é o seu motivo para não usar o jQuery? É praticamente insubstituível em situações como esta ...
James Hay
@hay nem um pouco, você pode até selecionar esses elementos em css puro também.
Knu
1
@ JamesHay porque nem todo ambiente, empresa, site, padrão de codificação, o que você tem, permite o uso do jQuery. O jQuery não é insubstituível.
Carnix
1
Eu ainda não vejo qualquer resposta que realmente funciona em diferentes data- elementos, ou seja: data-foo=0e data-bar=1 e data-app="js" e data-date="20181231"
Alex

Respostas:

244
document.querySelectorAll("[data-foo]")

você receberá todos os elementos com esse atributo.

document.querySelectorAll("[data-foo='1']")

você receberá apenas aqueles com o valor 1.

Joseph Marikle
fonte
Como você pode definir os valores para os elementos que obtém?
Steven Aguilar
@StevenAguilar .querySelectorAll()retorna a NodeList. Conforme observado nessa documentação, é possível iterar sobre a coleção usando .forEach(). Observe que esta é uma solução não-IE: developer.mozilla.org/en-US/docs/Web/API/… . Se você precisar dar suporte ao IE, precisará fazer um loop sobre o NodeList usando um forloop regular .
Joseph Marikle 23/12/19
13

Experimente → aqui

    <!DOCTYPE html>
    <html>
        <head></head>
        <body>
            <p data-foo="0"></p>
            <h6 data-foo="1"></h6>
            <script>
                var a = document.querySelectorAll('[data-foo]');

                for (var i in a) if (a.hasOwnProperty(i)) {
                    alert(a[i].getAttribute('data-foo'));
                }
            </script>
        </body>
    </html>
shawndumas
fonte
Usando hasOwnProperty é a melhor resposta para mim até agora em 2016, este é muito a respeito rápida outras maneiras de iteração Mdn hasOwnProperty
NVRM
NodeList de querySelectorAll () é iterável (embora não seja uma matriz). O loop com for initerará sobre as propriedades de comprimento e item. Em vez disso, use for ofpara iterar sobre as propriedades projetadas para serem iteradas
Solvitieg
1

Aqui está uma solução interessante: ele usa o mecanismo CSS dos navegadores para adicionar uma propriedade fictícia aos elementos correspondentes ao seletor e, em seguida, avalia o estilo calculado para encontrar os elementos correspondentes:

Ele cria dinamicamente uma regra de estilo e, em seguida, [...] varre o documento inteiro (usando o document.all muito criticado e específico do IE, mas muito rápido) e obtém o estilo calculado para cada um dos elementos. Em seguida, procuramos a propriedade foo no objeto resultante e verificamos se ela é avaliada como "barra". Para cada elemento que corresponde, adicionamos a uma matriz.

Heinrich Ulbricht
fonte
1
Certo, removi a dica sobre navegadores antigos.
Heinrich Ulbricht
Muito obrigado, senhor;) Devo confessar que negligenciei o 5.
Heinrich Ulbricht
É fácil perder a etiqueta. porque é html5, todos sugerimos document.querySelectorAll (e o atributo data- * também é específico para html5).
shawndumas
-1
var matches = new Array();

var allDom = document.getElementsByTagName("*");
for(var i =0; i < allDom.length; i++){
    var d = allDom[i];
    if(d["data-foo"] !== undefined) {
         matches.push(d);
    }
}

Não sei quem me enganou com -1, mas aqui está a prova.

http://jsfiddle.net/D798K/2/

Brian
fonte
3
seu principalmente "certo" apenas não está correto. Tenho certeza que alguém lhe deu o -1 porque você está fazendo um trabalho extra para obter os elementos e, em seguida, colocando a coleção em uma matriz. Eu não dei ao -1 apenas antipatia quando não há explicação para um.
Loktar 16/08
1
caro (todos os elementos na página), também use a notação literal da matriz (ou seja, []) e, além disso, ela não funciona. veja por si mesmo -> jsbin.com/ipisul/edit#javascript,html
shawndumas
2
Embora o OP esteja usando o HTML 5 de qualquer maneira, getElementsByTagNameum *seletor global ( ) é quebrado nas versões mais antigas do IE. É aqui que uma pesquisa DOM recursiva realiza o trabalho. Também não há propriedade "data-foo" em um ElementNode que é mapeado a partir do data-fooatributo. Você está procurando o datasetobjeto (ou seja: node.dataset.foo.
@shawndumas - parece que você estava tendo um PEBKAC. jsfiddle.net/D798K/2 . Funciona. Em última análise, eu -1-me para esta resposta de qualquer maneira - eu perdi as palavras "mais eficiente" na pergunta do OP ...
Brian
@Brian - o jsbin.com/ipisul funciona para você? Porque seu uma jsFiddle não está funcionando no meu (local de trabalho exigiu) IE9 ...
shawndumas
-4

Embora não seja tão bonita quanto querySelectorAll(que tem uma série de problemas), aqui está uma função muito flexível que repete o DOM e deve funcionar na maioria dos navegadores (antigos e novos). Enquanto o navegador suportar sua condição (por exemplo: atributos de dados), você poderá recuperar o elemento.

Para os curiosos: não se preocupe em testar isso vs. QSA no jsPerf. Navegadores como o Opera 11 armazenam em cache a consulta e inclinam os resultados.

Código:

function recurseDOM(start, whitelist)
{
    /*
    *    @start:        Node    -    Specifies point of entry for recursion
    *    @whitelist:    Object  -    Specifies permitted nodeTypes to collect
    */

    var i = 0, 
    startIsNode = !!start && !!start.nodeType, 
    startHasChildNodes = !!start.childNodes && !!start.childNodes.length,
    nodes, node, nodeHasChildNodes;
    if(startIsNode && startHasChildNodes)
    {       
        nodes = start.childNodes;
        for(i;i<nodes.length;i++)
        {
            node = nodes[i];
            nodeHasChildNodes = !!node.childNodes && !!node.childNodes.length;
            if(!whitelist || whitelist[node.nodeType])
            {
                //condition here
                if(!!node.dataset && !!node.dataset.foo)
                {
                    //handle results here
                }
                if(nodeHasChildNodes)
                {
                    recurseDOM(node, whitelist);
                }
            }
            node = null;
            nodeHasChildNodes = null;
        }
    }
}

Você pode iniciá-lo com o seguinte:

recurseDOM(document.body, {"1": 1}); por velocidade, ou apenas recurseDOM(document.body);

Exemplo com sua especificação: http://jsbin.com/unajot/1/edit

Exemplo com especificação diferente: http://jsbin.com/unajot/2/edit


fonte
23
Qual é a litania de problemas querySelectorAll?
ShreevatsaR
9
Eu também adoraria ouvir sobre essas questões.
21716 Sean_A91
4
Agora, nunca saberemos qual litania foi essa. Mais um capítulo para os Mistérios Eternos da SO
brasofilo 30/01/19
voto negativo isso. É completamente codificado e desnecessário com o querySelectorAllapi
dman