¿Cuándo usar setAttribute vs .attribute = en JavaScript?

234

Tiene una práctica recomendada para usar en setAttributelugar del punto (. notación de atributo )?

P.ej:

myObj.setAttribute("className", "nameOfClass");
myObj.setAttribute("id", "someID");

o

myObj.className = "nameOfClass";
myObj.id = "someID";
Francisc
fuente
1
Cuando cambié de .setAttribute()a [key] = value, todo comenzó a funcionar mágicamente.
Andrew

Respuestas:

73

Siempre debes usar el directo .attribute forma (pero vea el enlace del modo peculiar a continuación) si desea acceso programático en JavaScript. Debe manejar los diferentes tipos de atributos (piense "onload") correctamente.

Use getAttribute/ setAttributecuando desee tratar con el DOM tal como está (por ejemplo, solo texto literal). Diferentes navegadores confunden a los dos. Ver modos peculiares: compatibilidad de atributo (in) .


fuente
127
Esta respuesta no es lo suficientemente clara ... Realmente no siento que entiendo esto todavía.
temporary_user_name
1
@Aerovistae: de acuerdo contigo en esto. Se agregó una nueva respuesta que es de esperar más clara.
Olan
1
Pero si desea afectar el innerHTML del elemento, debe usar setAttribute ...
Michael
3
Te refieres a outterHTML * :)
megawac
44
Descubrí que a.href devuelve la url completa, pero getAttribute ('href') devuelve exactamente qué en ese atributo (<a href = "/ help" ...).
Conejo de plástico
144

Desde Javascript: la guía definitiva , aclara las cosas. Señala que HTMLElement objetos de un documento HTML definen propiedades JS que corresponden a todos los atributos HTML estándar.

Por lo tanto, solo necesita usar setAttributepara atributos no estándar.

Ejemplo:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
Olan
fuente
2
y además, aparece después de que el último setAttribute en su ejemplo node.frameborderNO esté definido, por lo que debe obtenerAttribute para recuperar el valor.
Michael
55
@Michael correcto: si usa setAttribute para establecer un valor, debe usar getAttribute para recuperarlo.
Olan
3
No hay nada de malo en configurar frameBorderdirectamente, pero tenga en cuenta la capitalización. Alguien pensó que era una buena idea camelCase los equivalentes de JavaScript de los atributos HTML. No he logrado encontrar ninguna especificación para esto, pero la red parece estar de acuerdo en que se trata de 12 casos específicos (al menos para HTML 4). Consulte, por ejemplo, la siguiente publicación: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa
1
El usemapatributo no se puede establecer utilizando la notación de puntos al crear el mapa dinámicamente para una imagen. Requiere img.setAttribute('usemap', "#MapName");¿Su respuesta implica que, usemappor lo tanto, es "no estándar"?
mseifert
1
Esto en su mayoría está mal. Algunos atributos tienen propiedades definidas, por lo que no. Realmente se trata de cómo escribieron las especificaciones. No tiene nada que ver con los atributos estándar o no. Sin embargo, es cierto que solo se puede acceder a las propiedades no estándar con getAttribute ().
Ben
79

Ninguna de las respuestas anteriores está completa y la mayoría contiene información errónea.

Hay tres formas de acceder a los atributos de un elemento DOM en JavaScript. Los tres funcionan de manera confiable en los navegadores modernos siempre que comprenda cómo utilizarlos.

1) element.attributes

Los elementos tienen atributos de propiedad que devuelve un NamedNodeMap de objetos Attr en vivo . Los índices de esta colección pueden ser diferentes entre los navegadores. Por lo tanto, el pedido no está garantizado. NamedNodeMaptiene métodos para agregar y eliminar atributos ( getNamedItemy setNamedItem, respectivamente).

Tenga en cuenta que aunque XML explícitamente distingue entre mayúsculas y minúsculas, la especificación DOM requiere que los nombres de cadena se normalicen , por lo que los nombres pasados ​​no getNamedItemdistinguen entre mayúsculas y minúsculas.

Ejemplo de uso:

var div = document.getElementsByTagName('div')[0];

//you can look up specific attributes
var classAttr = div.attributes.getNamedItem('CLASS');
document.write('attributes.getNamedItem() Name: ' + classAttr.name + ' Value: ' + classAttr.value + '<br>');

//you can enumerate all defined attributes
for(var i = 0; i < div.attributes.length; i++) {
  var attr = div.attributes[i];
  document.write('attributes[] Name: ' + attr.name + ' Value: ' + attr.value + '<br>');
}

//create custom attribute
var customAttr = document.createAttribute('customTest');
customAttr.value = '567';
div.attributes.setNamedItem(customAttr);

//retreive custom attribute
customAttr = div.attributes.getNamedItem('customTest');
document.write('attributes.getNamedItem() Name: ' + customAttr.name + ' Value: ' + customAttr.value + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

2. element.getAttribute&element.setAttribute

Estos métodos existen directamente en el Elementsin necesidad de acceder attributesy sus métodos, pero realizan las mismas funciones.

Nuevamente, observe que el nombre de la cadena no distingue entre mayúsculas y minúsculas.

Ejemplo de uso:

var div = document.getElementsByTagName('div')[0];

//get specific attributes
document.write('Name: class Value: ' + div.getAttribute('class') + '<br>');
document.write('Name: ID Value: ' + div.getAttribute('ID') + '<br>');
document.write('Name: DATA-TEST Value: ' + div.getAttribute('DATA-TEST') + '<br>');
document.write('Name: nonStandard Value: ' + div.getAttribute('nonStandard') + '<br>');


//create custom attribute
div.setAttribute('customTest', '567');

//retreive custom attribute
document.write('Name: customTest Value: ' + div.getAttribute('customTest') + '<br>');
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

3. Propiedades en el objeto DOM, como element.id

Se puede acceder a muchos atributos utilizando propiedades convenientes en el objeto DOM. Los atributos que existen dependen del tipo de nodo DOM, no de qué atributos están definidos en el HTML. Las propiedades se definen en algún lugar de la cadena de prototipos del objeto DOM en cuestión. Las propiedades específicas definidas dependerán del tipo de elemento al que esté accediendo. Por ejemplo, classNamey idse definen Elementy existen en todos los nodos DOM que son elementos (es decir, no nodos de texto o comentario). Pero valuees más estrecho. Está definido HTMLInputElementy puede no existir en otros elementos.

Tenga en cuenta que las propiedades de JavaScript distinguen entre mayúsculas y minúsculas. Aunque la mayoría de las propiedades usarán minúsculas, algunas son camelCase. Así que siempre verifique las especificaciones para estar seguro.

Este "gráfico" captura una parte de la cadena de prototipo para estos objetos DOM. Ni siquiera está cerca de completarse, pero captura la estructura general.

                      ____________Node___________
                      |               |         |
                   Element           Text   Comment
                   |     |
           HTMLElement   SVGElement
           |         |
HTMLInputElement   HTMLSpanElement

Ejemplo de uso:

var div = document.getElementsByTagName('div')[0];

//get specific attributes
document.write('Name: class Value: ' + div.className + '<br>');
document.write('Name: id Value: ' + div.id + '<br>');
document.write('Name: ID Value: ' + div.ID + '<br>'); //undefined
document.write('Name: data-test Value: ' + div.dataset.test + '<br>'); //.dataset is a special case
document.write('Name: nonStandard Value: ' + div.nonStandard + '<br>'); //undefined
<div class="class1" id="main" data-test="stuff" nonStandard="1234"></div>

Advertencia: esta es una explicación de cómo las especificaciones HTML definen y los navegadores modernos manejan los atributos. No intenté lidiar con las limitaciones de los antiguos navegadores rotos. Si necesita admitir navegadores antiguos, además de esta información, necesitará saber qué está roto en esos navegadores.

Ben
fuente
Gracias por aclarar esto. Tengo curiosidad, ¿qué versiones de IE se consideran 'modernas' y siguen las especificaciones HTML?
jkdev
3
@jkdev IE nunca es moderno. Lo que sea que se haga viejo.
Suraj Jain
Gracias por una respuesta tan detallada, leí mucho sobre DOM y herencia como HTMLElement heredar de Element y así sucesivamente, su respuesta tiene mucho sentido.
Suraj Jain
16

Un caso que encontré donde setAttributees necesario es al cambiar los atributos ARIA, ya que no hay propiedades correspondientes. Por ejemplo

x.setAttribute('aria-label', 'Test');
x.getAttribute('aria-label');

No hay x.arialabel nada ni nada por el estilo, por lo que debe usar setAttribute.

Editar: x ["etiqueta aria"] no funciona . Realmente necesitas setAttribute.

x.getAttribute('aria-label')
null
x["aria-label"] = "Test"
"Test"
x.getAttribute('aria-label')
null
x.setAttribute('aria-label', 'Test2')
undefined
x["aria-label"]
"Test"
x.getAttribute('aria-label')
"Test2"
Antimonio
fuente
en realidad no realmente en Javascript puedes hacer esto x ["etiqueta aria"]
Fareed Alnamrouti
@fareednamrouti Eso no funciona. Lo acabo de probar. Las propiedades JS no afectan los atributos html. Realmente necesitas setAttribute aquí.
Antimonio
@Antimony Esto es extraño, pero sí, tienes 100% de razón, votaré
Fareed Alnamrouti
2
¿Estás seguro de que no hay ariaLabel?
jgmjgm
8

Estas respuestas no abordan realmente la gran confusión entre las propiedades y los atributos . Además, dependiendo del prototipo de Javascript, a veces puedes usar la propiedad de un elemento para acceder a los atributos y otras veces no.

Primero, debe recordar que an HTMLElementes un objeto Javascript. Como todos los objetos, tienen propiedades. Claro, puede crear una propiedad llamada casi cualquier cosa que desee dentro HTMLElement, pero no tiene que hacer nada con el DOM (lo que está en la página). La notación de puntos ( .) es para propiedades . Ahora, hay algunas propiedades especiales . que se asignan a los atributos, y en el momento de la escritura solo hay 4 que están garantizadas (más sobre eso más adelante).

Todos los HTMLElements incluyen una propiedad llamada attributes. HTMLElement.attributeses un objeto vivo NamedNodeMap que se relaciona con los elementos en el DOM. "En vivo" significa que cuando el nodo cambia en el DOM, cambian en el lado de JavaScript, y viceversa. Los atributos DOM, en este caso, son los nodos en cuestión. A Nodetiene una .nodeValuepropiedad que puede cambiar. NamedNodeMapLos objetos tienen una función llamada setNamedItemdonde puede cambiar todo el nodo. También puede acceder directamente al nodo con la tecla. Por ejemplo, puede decir .attributes["dir"]cuál es el mismo que .attributes.getNamedItem('dir');(Nota NamedNodeMapal margen, no distingue entre mayúsculas y minúsculas, por lo que también puede pasar 'DIR');

Hay una función similar directamente en HTMLElementdonde puede llamar, setAttributeque creará automáticamente un nodo si no existe y establecerá el nodeValue. También hay algunos atributos a los que puede acceder directamente como propiedades a HTMLElementtravés de propiedades especiales , como dir. Aquí hay un mapeo aproximado de cómo se ve:

HTMLElement {
  attributes: {
    setNamedItem: function(attr, newAttr) { 
      this[attr] = newAttr;
    },    
    getNamedItem: function(attr) {
      return this[attr];
    },
    myAttribute1: {
      nodeName: 'myAttribute1',
      nodeValue: 'myNodeValue1'
    },
    myAttribute2: {
      nodeName: 'myAttribute2',
      nodeValue: 'myNodeValue2'
    },
  }
  setAttribute: function(attr, value) { 
    let item = this.attributes.getNamedItem(attr);
    if (!item) {
      item = document.createAttribute(attr);
      this.attributes.setNamedItem(attr, item);
    }
    item.nodeValue = value;
  },
  getAttribute: function(attr) { 
    return this.attributes[attr] && this.attributes[attr].nodeValue;
  },
  dir: // Special map to attributes.dir.nodeValue || ''
  id:  // Special map to attributes.id.nodeValue || ''
  className: // Special map to attributes.class.nodeValue || '' 
  lang: // Special map to attributes.lang.nodeValue || ''

}

Para que pueda cambiar los diratributos de 6 maneras:

  // 1. Replace the node with setNamedItem
  const newAttribute = document.createAttribute('dir');
  newAttribute.nodeValue = 'rtl';
  element.attributes.setNamedItem(newAttribute);

  // 2. Replace the node by property name;
  const newAttribute2 = document.createAttribute('dir');
  newAttribute2.nodeValue = 'rtl';
  element.attributes['dir'] = newAttribute2;
  // OR
  element.attributes.dir = newAttribute2;

  // 3. Access node with getNamedItem and update nodeValue
  // Attribute must already exist!!!
  element.attributes.getNamedItem('dir').nodeValue = 'rtl';

  // 4. Access node by property update nodeValue
  // Attribute must already exist!!!
  element.attributes['dir'].nodeValue = 'rtl';
  // OR
  element.attributes.dir.nodeValue = 'rtl';

  // 5. use setAttribute()  
  element.setAttribute('dir', 'rtl');
  
  // 6. use the UNIQUELY SPECIAL dir property
  element["dir"] = 'rtl';
  element.dir = 'rtl';

Puede actualizar todas las propiedades con métodos # 1-5, pero sólo dir, id, lang, y classNamecon el método # 6.

Extensiones de HTMLElement

HTMLElementtiene esas 4 propiedades especiales. Algunos elementos son clases extendidas o HTMLElementtienen incluso más propiedades mapeadas. Por ejemplo, HTMLAnchorElementtiene HTMLAnchorElement.href, HTMLAnchorElement.rel, y HTMLAnchorElement.target. Pero tenga cuidado , si establece esas propiedades en elementos que no tienen esas propiedades especiales (como en a HTMLTableElement), entonces los atributos no se cambian y son solo propiedades personalizadas normales. Para comprender mejor, aquí hay un ejemplo de su herencia:

HTMLAnchorElement extends HTMLElement {
  // inherits all of HTMLElement
  href:    // Special map to attributes.href.nodeValue || ''
  target:  // Special map to attributes.target.nodeValue || ''
  rel:     // Special map to attributes.ref.nodeValue || '' 
}

Propiedades personalizadas

Ahora la gran advertencia: al igual que todos los objetos Javascript , puede agregar propiedades personalizadas. Pero, esos no cambiarán nada en el DOM. Tu puedes hacer:

  const newElement = document.createElement('div');
  // THIS WILL NOT CHANGE THE ATTRIBUTE
  newElement.display = 'block';

Pero eso es lo mismo que

  newElement.myCustomDisplayAttribute = 'block';

Esto significa que no se vinculará la.attributes[attr].nodeValue adición de una propiedad personalizada .

Actuación

He creado un caso de prueba jsperf para mostrar la diferencia: https://jsperf.com/set-attribute-comparison . Básicamente, en orden:

  1. Propiedades personalizadas porque no afectan al DOM y no son atributos .
  2. Asignaciones especiales proporcionados por el navegador ( dir, id, className).
  3. Si los atributos ya existen ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Si los atributos ya existen ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Conclusión (TL; DR)

  • Utilizar las asignaciones de propiedades especiales de HTMLElement: element.dir, element.id, element.className, o element.lang.

  • Si está 100% seguro de que el elemento es extendido HTMLElementcon una propiedad especial, use esa asignación especial. (Puedes consultar con if (element instanceof HTMLAnchorElement)).

  • Si está 100% seguro de que el atributo ya existe, use element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Si no, úsalo setAttribute().

Mecha corta
fuente
Usted mencionó estas cuatro asignaciones de propiedades: dir, id, className y lang. ¿Qué pasa con classList? ¿ClassList es una asignación de propiedad que se garantiza que existe?
Barzee
classListestá 100% garantizado de existir, pero no es una propiedad de cadena, es un DOMTokenListobjeto vivo . Establecer .classNamedirectamente es más rápido que manipular classList, pero sobrescribiría todo.
ShortFuse
¿Qué tal .value en las etiquetas <input> y <textarea>? ¿De qué tipo son?
Daniel Williams
Los que se mencionan en la respuesta son lo que W3C llama "atributos IDL que reflejan". Cuando cambia .value, está cambiando el valor interno de HTMLInputElement, que luego se refleja en los atributos. Tampoco tienen que serlo string. .valueAsNumbercambiará value internamente , y su stringforma aparecerá en el valueatributo. developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse
3

"¿Cuándo usar setAttribute vs .attribute = en JavaScript?"

Una regla general es usar .attributey verificar si funciona en el navegador.

.. Si funciona en el navegador, ya está listo.

Si no es así, use en .setAttribute(attribute, value)lugar de .attributepara ese atributo.

Enjuague y repita para todos los atributos.

Bueno, si eres flojo, simplemente puedes usarlo .setAttribute. Eso debería funcionar bien en la mayoría de los navegadores. (Aunque los navegadores que admiten .attributepueden optimizarlo mejor que .setAttribute(attribute, value)).

Pacerier
fuente
0

Este parece un caso en el que es mejor usar setAttribute:

Dev.Opera - JavaScript eficiente

var posElem = document.getElementById('animation');
var newStyle = 'background: ' + newBack + ';' +
'color: ' + newColor + ';' +
    'border: ' + newBorder + ';';
if(typeof(posElem.style.cssText) != 'undefined') {
    posElem.style.cssText = newStyle;
} else {
    posElem.setAttribute('style', newStyle);
}
tomo7
fuente
2
Gracias por compartir este tomo7, ¿podría explicar un poco más? ¿ posElem.style = newStyleNo funciona en todos los navegadores (funcionó para mí en Firefox)? ¿Es solo por razones de rendimiento lo que setAttributese prefiere, evitando las repintado? ¿Es posElem.style.cssText = newStylemás perfumante entonces posElem.style = newStyle?
Noitidart
0

métodos para establecer atributos (por ejemplo, clase) en un elemento: 1. el.className = string 2. el.setAttribute ('class', string) 3. el.attributes.setNamedItem (objeto) 4. el.setAttributeNode (nodo)

Hice una prueba de referencia simple ( aquí )

y parece que setAttributeNode es aproximadamente 3 veces más rápido que usar setAttribute.

así que si el rendimiento es un problema, use "setAttributeNode"

Yair Levy
fuente
Creo que ejecutas la prueba en Chrome. Probé mi Mac usando Chrome, Safari y Firefox; se esperaba que 3 de ellos mostraran 3 resultados diferentes.
Olgun Kaya el
0

Interesante extracción de la secuencia de comandos API de Google con respecto a esto:

Lo hacen así:

var scriptElement = document.createElement("script");
scriptElement = setAttribute("src", "https://some.com");
scriptElement = setAttribute("nonce", "https://some.com");
scriptElement.async = "true";

Observe cómo se usan setAttributepara "src" y "nonce", pero luego .async = ...para el atributo "async".

No estoy 100% seguro, pero probablemente se deba a que "async" solo es compatible con los navegadores que admiten la .attr =asignación directa . Entonces, no tiene sentido tratar desestAttribute("async") porque si el navegador no comprende .async=..., no entenderá el atributo "asíncrono".

Con suerte, esa es una idea útil de mi proyecto de investigación en curso "Un-minify GAPI" . Corrígeme si me equivoco.

Maxim Mazurok
fuente