¿Obtener elementos por atributo cuando querySelectorAll no está disponible sin usar bibliotecas?

123
<p data-foo="bar">

¿Cómo puedes hacer el equivalente a

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

donde querySelectorAll no está disponible ?

Necesito una solución nativa que funcione al menos en IE7. No me importa IE6.

ryanve
fuente
echa un vistazo a la biblioteca del selector de JavaScript sizzle.js
época
1
Bueno, sí, la única selección que necesito hacer son los atributos de datos, así que estaba tratando de encontrar la forma más sencilla de parchear eso sin tirar de un motor selector completo como Sizzle. Pero buen punto para mirar en la fuente. Por cierto, otro gran motor de selección es github.com/ded/qwery
ryanve
@ryanve, gracias echaré un vistazo :)
época
La solución de trabajo que utilicé está en github.com/ryanve/dope/blob/master/dope.js en el método llamado 'queryAttr'
ryanve
77
Lol, tu pregunta es mi respuesta. Entonces esto viene con otra pregunta. ¿En qué situación querySelectorAllno está disponible? note - I don't care all IE
vzhen

Respuestas:

136

Podría escribir una función que ejecute getElementsByTagName ('*') y devuelva solo aquellos elementos con un 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;
}

Luego,

getAllElementsWithAttribute('data-foo');
kevinfahy
fuente
8
Usar != nulles la forma ideal (mejor que mi comentario anterior) porque en el antiguo IE es posible que getAttribute devuelva un valor cuyo valor typeofes'number'
ryanve
1
¿Por qué usar en document.getElementsByTagName('*')lugar de document.all?
pedrozath
1
¿Por qué no utilizar en hasAttributelugar de getAttribute() !== null, ya que solo desea verificar la existencia y no su valor?
rvighne
61

Utilizar

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

o

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

para encontrar elementos por atributo. Ahora es compatible con todos los navegadores relevantes (incluso IE8): http://caniuse.com/#search=queryselector

Pylinux
fuente
2
¿Cómo tiene esto tantos votos positivos cuando la pregunta explícitamente pregunta: "Necesito una solución nativa que funcione al menos en IE7 "? En segundo lugar, ese enlace indica que el soporte comienza en IE11 aunque en realidad comienza desde IE8; tal vez esto debería cambiarse a developer.mozilla.org/en-US/docs/Web/API/Element/…, por lo que en realidad respalda la respuesta. ..?
Zze
77
La razón de todos los votos a favor, y la razón por la que hice la respuesta, es que esta pregunta SO es realmente antigua, por lo que cuando busca cómo encontrar elementos DOM, encuentra esta pregunta muy alta en los resultados de búsqueda, y dado que es lo que la gente están buscando que voten. Utilidad> precisión histórica. En segundo lugar, el enlace aún funciona bien, es solo que caniuse.com ha ocultado los navegadores antiguos, si cambia a "Uso relativo", todavía verá los navegadores antiguos.
Pylinux
Funcionó a la perfección. Rápido y simple
Dawson B
Es 2020. Esta debería ser la respuesta aceptada ahora.
NearHuscarl
44

Jugué un poco y terminé con esta solución cruda:

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

El uso es bastante simple y funciona incluso en IE8:

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

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

Pero recomiendo usar querySelector/ Allpara esto (y para admitir navegadores antiguos, use un polyfill ):

document.querySelectorAll('[data-foo]');
yckart
fuente
Sí, +1 para querySelectorAll. Una prueba rápida de jsperf jsperf.com/custom-vs-selectorall-attributes muestra que es mucho más rápido que la respuesta aceptada ... desafortunadamente no es compatible con IE 7 :(
Sebastien Daniel
11

Prueba esto funciona

document.querySelector ('[atributo = "valor"]')

ejemplo:

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

Eso también funciona:

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

Entonces:

document.querySelector([data-foo="bar"]);
CallMarl
fuente
2
Faltan las comillas simples en el querySelector real. Debería ser: document.querySelector('[data-foo="bar"]');
Brettins
1

Prueba esto: cambié ligeramente las respuestas anteriores:

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

Luego,

getAttributes('data-foo');
Surender Lohia
fuente
3
¿Qué cambiaste y por qué?
Artjom B.
1

Una pequeña modificación en la respuesta de @kevinfahy , para permitir obtener el atributo por valor si es necesario:

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
fuente
0

No usar en el navegador

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

Pero si está realizando pruebas unitarias y su dom simulado tiene una implementación de querySelector flakey, esto hará el truco.

Esta es la respuesta de @ kevinfahy, simplemente recortada para ser un poco con las funciones de flecha de grasa ES6 y convirtiendo HtmlCollection en una matriz a costa de la legibilidad, tal vez.

Por lo tanto, solo funcionará con un transpilador ES6. Además, no estoy seguro de qué tan eficiente será con muchos elementos.

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

Y aquí hay una variante que obtendrá un atributo con un valor específico

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