Obter elementos por atributo quando querySelectorAll não está disponível sem o uso de bibliotecas?

123
<p data-foo="bar">

Como você pode fazer o equivalente a

document.querySelectorAll('[data-foo]')

onde querySelectorAll não está disponível ?

Eu preciso de uma solução nativa que funcione pelo menos no IE7. Eu não ligo para o IE6.

ryanve
fonte
confira a biblioteca de seletores javascript sizzle.js
época
1
Bom, sim, a única seleção que preciso fazer é atributos de dados, então eu estava tentando descobrir a maneira mais simples de corrigir isso sem puxar um mecanismo de seleção inteiro como o Sizzle. Mas bom ponto de olhar na fonte. BTW outro mecanismo selector grande é github.com/ded/qwery
ryanve
@ryanve, graças eu vou dar uma olhada :)
época
A solução de trabalho que eu usei está em github.com/ryanve/dope/blob/master/dope.js no método chamado 'queryAttr'
ryanve
7
Lol, sua pergunta é minha resposta. Então isso vem com outra pergunta. Em que situação querySelectorAllnão está disponível? note - I don't care all IE
vzhen

Respostas:

136

Você pode escrever uma função que execute getElementsByTagName ('*') e retorne apenas os elementos com o atributo "data-foo":

function getAllElementsWithAttribute(attribute)
{
  var matchingElements = [];
  var allElements = document.getElementsByTagName('*');
  for (var i = 0, n = allElements.length; i < n; i++)
  {
    if (allElements[i].getAttribute(attribute) !== null)
    {
      // Element exists with attribute. Add to array.
      matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}

Então,

getAllElementsWithAttribute('data-foo');
kevinfahy
fonte
8
Usando != nullé a maneira ideal (melhor do que o meu comentário acima) porque no velho IE é possível para getAttribute para retornar um valor cuja typeofé'number'
ryanve
1
Por que usar em document.getElementsByTagName('*')vez de document.all?
214166 Pedrozath
1
Por que não usar, hasAttributee não getAttribute() !== null, já que você deseja apenas verificar a existência e não o seu valor?
rvighne
61

Usar

//find first element with "someAttr" attribute
document.querySelector('[someAttr]')

ou

//find all elements with "someAttr" attribute
document.querySelectorAll('[someAttr]') 

para encontrar elementos por atributo. Agora ele é suportado em todos os navegadores relevantes (mesmo no IE8): http://caniuse.com/#search=queryselector

Pylinux
fonte
2
Como isso tem tantos votos positivos quando a pergunta solicita explicitamente: "Preciso de uma solução nativa que funcione pelo menos no IE7 ". Em segundo lugar, esse link indica que o suporte começa no IE11, mesmo que ele realmente inicie no IE8 - talvez isso deva ser trocado por developer.mozilla.org/en-US/docs/Web/API/Element/…, para dar suporte à resposta. ..?
Zze 12/09
7
O motivo de todos os votos positivos, e o motivo pelo qual eu respondi, é que essa pergunta do SO é realmente antiga; portanto, quando você pesquisa como encontrar elementos DOM, essa pergunta é muito alta nos resultados da pesquisa, e é o que as pessoas estão procurando por votos positivos. Utilidade> precisão histórica. Em segundo lugar, o link ainda funciona bem, mas o caniuse.com oculta navegadores antigos; se você alternar para "Uso relativo", ainda verá os navegadores antigos.
Pylinux 13/09
Funcionou perfeitamente. Rápido e simples
Dawson B
É 2020. Essa deve ser a resposta aceita agora.
NearHuscarl
44

Eu brinquei um pouco e acabei com esta solução grosseira:

function getElementsByAttribute(attribute, context) {
  var nodeList = (context || document).getElementsByTagName('*');
  var nodeArray = [];
  var iterator = 0;
  var node = null;

  while (node = nodeList[iterator++]) {
    if (node.hasAttribute(attribute)) nodeArray.push(node);
  }

  return nodeArray;
}

O uso é bastante simples e funciona mesmo no IE8:

getElementsByAttribute('data-foo');
// or with parentNode
getElementsByAttribute('data-foo', document);

http://fiddle.jshell.net/9xaxf6jr/

Mas eu recomendo usar querySelector/ Allpara isso (e para suportar navegadores mais antigos, use um polyfill ):

document.querySelectorAll('[data-foo]');
yckart
fonte
Sim, +1 para querySelectorAll. Um teste rápido do jsperf jsperf.com/custom-vs-selectorall-attributes mostra que é muito mais rápido do que a resposta aceita ... infelizmente não é compatível com o IE 7 :(
Sebastien Daniel
11

Tente isso funciona

document.querySelector ('[attribute = "value"]')

exemplo:

document.querySelector('[role="button"]')
BrainabilGH
fonte
5

Isso também funciona:

document.querySelector([attribute="value"]);

Assim:

document.querySelector([data-foo="bar"]);
CallMarl
fonte
2
Faltam aspas simples no querySelector real. Deve ser: document.querySelector('[data-foo="bar"]');
Brettins
1

Tente isso - alterei ligeiramente as respostas acima:

var getAttributes = function(attribute) {
    var allElements = document.getElementsByTagName('*'),
        allElementsLen = allElements.length,
        curElement,
        i,
        results = [];

    for(i = 0; i < allElementsLen; i += 1) {
        curElement = allElements[i];

        if(curElement.getAttribute(attribute)) {
            results.push(curElement);
        }
    }

    return results;
};

Então,

getAttributes('data-foo');
Surender Lohia
fonte
3
O que você mudou e por quê?
Artjom B.
1

Uma pequena modificação na resposta de @kevinfahy , para permitir a obtenção do atributo por valor, se necessário:

function getElementsByAttributeValue(attribute, value){
  var matchingElements = [];
  var allElements = document.getElementsByTagName('*');
  for (var i = 0, n = allElements.length; i < n; i++) {
    if (allElements[i].getAttribute(attribute) !== null) {
      if (!value || allElements[i].getAttribute(attribute) == value)
        matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}
Z. Khullah
fonte
0

Não use no navegador

No navegador, use document.querySelect('[attribute-name]').

Mas se você estiver testando a unidade e o seu Dom zombado tiver uma implementação flakey querySelector, isso funcionará.

Esta é a resposta de @ kevinfahy, reduzida para ser um pouco com as funções de seta gorda ES6 e convertendo o HtmlCollection em uma matriz, talvez com o custo de legibilidade.

Portanto, ele funcionará apenas com um transpiler ES6. Além disso, não tenho certeza de qual será o desempenho de muitos elementos.

function getElementsWithAttribute(attribute) {
  return [].slice.call(document.getElementsByTagName('*'))
    .filter(elem => elem.getAttribute(attribute) !== null);
}

E aqui está uma variante que obterá um atributo com um valor específico

function getElementsWithAttributeValue(attribute, value) {
  return [].slice.call(document.getElementsByTagName('*'))
    .filter(elem => elem.getAttribute(attribute) === value);
}
gigantesco
fonte