Quand utiliser setAttribute vs .attribute = en JavaScript?

234

Une meilleure pratique d'utilisation setAttributede la .notation d'attribut dot ( ) a-t-elle été développée?

Par exemple:

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

ou

myObj.className = "nameOfClass";
myObj.id = "someID";
Francisc
la source
1
Quand je suis passé de .setAttribute()à [key] = value, tout a commencé comme par magie.
Andrew

Réponses:

73

Vous devez toujours utiliser le .attributeformulaire direct (mais voir le lien quirksmode ci-dessous) si vous voulez un accès programmatique en JavaScript. Il doit gérer correctement les différents types d'attributs (pensez "onload").

Utilisez getAttribute/ setAttributelorsque vous souhaitez traiter le DOM tel quel (par exemple, texte littéral uniquement). Différents navigateurs confondent les deux. Voir Modes Quirks: compatibilité (in) des attributs .


la source
127
Cette réponse n'est pas assez claire ... Je n'ai pas encore vraiment l'impression de comprendre cela.
temporary_user_name
1
@Aerovistae - d'accord avec vous sur ce point. Ajout d'une nouvelle réponse qui, je l'espère, est plus claire.
olan
1
Mais si vous voulez affecter le innerHTML de l'élément, vous devez utiliser setAttribute ...
Michael
3
Vous voulez dire outterHTML * :)
megawac
4
J'ai trouvé que a.href renvoie l'url complète, mais getAttribute ('href') renvoie exactement ce que contient cet attribut (<a href = "/ help" ...).
Plastic Rabbit
144

De Javascript: The Definitive Guide , il clarifie les choses. Il note que les objets HTMLElement d'un document HTML définissent des propriétés JS qui correspondent à tous les attributs HTML standard.

Il vous suffit donc d'utiliser setAttributedes attributs non standard.

Exemple:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
olan
la source
2
et en outre, il apparaît après le dernier setAttribute dans votre exemple, node.framebordern'est PAS défini, vous devez donc obtenir getAttribute pour récupérer la valeur.
Michael
5
@Michael correct - si vous utilisez setAttribute pour définir une valeur, vous devez utiliser getAttribute pour la récupérer.
olan
3
Il n'y a rien de mal à définir frameBorderdirectement, mais notez la capitalisation. Quelqu'un a pensé que c'était une bonne idée de camelCase les équivalents JavaScript des attributs HTML. Je n'ai pas réussi à trouver de spécification pour cela, mais le net semble être d'accord qu'il s'agit de 12 cas spécifiques (pour HTML 4 au moins). Voir par exemple le post suivant: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa
1
L' usemapattribut ne peut pas être défini à l'aide de la notation par points lors de la création dynamique de la carte pour une image. Cela nécessite img.setAttribute('usemap', "#MapName");Votre réponse implique-t-elle que usemapc'est donc "non standard"?
mseifert
1
C'est surtout faux. Certains attributs ont des propriétés définies, alors ne le faites pas. Il s'agit vraiment de la façon dont ils ont écrit les spécifications. Cela n'a rien à voir avec les attributs standard ou non. Cependant, il est vrai que les propriétés non standard ne sont accessibles qu'avec getAttribute ().
Ben
79

Aucune des réponses précédentes n'est complète et la plupart contiennent des informations erronées.

Il existe trois façons d'accéder aux attributs d'un élément DOM en JavaScript. Les trois fonctionnent de manière fiable dans les navigateurs modernes tant que vous comprenez comment les utiliser.

1. element.attributes

Les éléments ont des attributs de propriété qui retournent un NamedNodeMap en direct d' objets Attr . Les index de cette collection peuvent être différents selon les navigateurs. Ainsi, la commande n'est pas garantie. NamedNodeMapa des méthodes pour ajouter et supprimer des attributs ( getNamedItemet setNamedItem, respectivement).

Notez que bien que XML soit explicitement sensible à la casse, la spécification DOM appelle à la normalisation des noms de chaîne , de sorte que les noms passés à getNamedItemne respectent pas la casse.

Exemple d'utilisation:

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

Ces méthodes existent directement sur le Elementsans avoir besoin d'accéder à attributesses méthodes mais remplissent les mêmes fonctions.

Encore une fois, notez que le nom de chaîne n'est pas sensible à la casse.

Exemple d'utilisation:

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. Propriétés sur l'objet DOM, telles que element.id

De nombreux attributs sont accessibles à l'aide de propriétés pratiques sur l'objet DOM. Les attributs existants dépendent du type du nœud DOM, et non des attributs définis dans le HTML. Les propriétés sont définies quelque part dans la chaîne prototype de l'objet DOM en question. Les propriétés spécifiques définies dépendront du type d'élément auquel vous accédez. Par exemple, classNameet idsont définis sur Elementet existent sur tous les nœuds DOM qui sont des éléments (c'est-à-dire pas des nœuds de texte ou de commentaire). Mais valueest plus étroit. Il est défini sur HTMLInputElementet peut ne pas exister sur d'autres éléments.

Notez que les propriétés JavaScript sont sensibles à la casse. Bien que la plupart des propriétés utilisent des minuscules, certaines sont camelCase. Vérifiez donc toujours les spécifications pour en être sûr.

Ce "graphique" capture une partie de la chaîne de prototype pour ces objets DOM. Ce n'est même pas près d'être terminé, mais il capture la structure globale.

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

Exemple d'utilisation:

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>

Mise en garde: il s'agit d'une explication de la façon dont la spécification HTML définit et les navigateurs modernes gèrent les attributs. Je n'ai pas tenté de faire face aux limitations des anciens navigateurs brisés. Si vous devez prendre en charge d'anciens navigateurs, en plus de ces informations, vous devrez savoir ce qui est cassé dans ces navigateurs.

Ben
la source
Merci d'avoir clarifié cela. Je suis curieux de savoir quelles versions d'IE sont considérées comme «modernes» et suivent les spécifications HTML?
jkdev
3
@jkdev IE n'est jamais moderne. Quoi que ça devienne vieux.
Suraj Jain
Merci pour cette réponse si détaillée, j'ai beaucoup lu sur DOM et l'héritage comme HTMLElement hérité d'Element et ainsi de suite, votre réponse est parfaitement logique.
Suraj Jain
16

J'ai trouvé un cas où il setAttributeest nécessaire de modifier les attributs ARIA, car il n'y a pas de propriétés correspondantes. Par exemple

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

Il n'y a x.arialabelrien ou quelque chose comme ça, vous devez donc utiliser setAttribute.

Edit: x ["aria-label"] ne fonctionne pas . Vous avez vraiment besoin de 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"
Antimoine
la source
en fait pas vraiment en Javascript vous pouvez le faire x ["aria-label"]
Fareed Alnamrouti
@fareednamrouti Cela ne fonctionne pas. Je viens de le tester. Les propriétés JS n'affectent pas les attributs html. Vous avez vraiment besoin de setAttribute ici.
Antimony
@Antimony C'est étrange mais oui, vous avez 100% raison, je voterai
Fareed Alnamrouti
2
Êtes-vous sûr qu'il n'y a pas ariaLabel?
jgmjgm
8

Ces réponses ne traitent pas vraiment de la grande confusion entre les propriétés et les attributs . En outre, selon le prototype Javascript, vous pouvez parfois utiliser la propriété d'un élément pour accéder à des attributs et parfois vous ne pouvez pas.

Tout d'abord, vous devez vous rappeler que an HTMLElementest un objet Javascript. Comme tous les objets, ils ont des propriétés. Bien sûr, vous pouvez créer une propriété appelée presque tout ce que vous voulez à l'intérieur HTMLElement, mais cela n'a rien à voir avec le DOM (ce qui est sur la page). La notation par points ( .) est pour les propriétés . Maintenant, il y a des propriétés spéciales qui sont mappées aux attributs, et au moment de l'écriture, il n'y en a que 4 qui sont garantis (plus à ce sujet plus tard).

Tous les HTMLElements incluent une propriété appelée attributes. HTMLElement.attributesest un objet vivant NamedNodeMap qui se rapporte aux éléments du DOM. «Live» signifie que lorsque le nœud change dans le DOM, il change du côté JavaScript, et vice versa. Les attributs DOM, dans ce cas, sont les nœuds en question. A Nodepossède une .nodeValuepropriété que vous pouvez modifier. NamedNodeMaples objets ont une fonction appelée setNamedItemoù vous pouvez changer le nœud entier. Vous pouvez également accéder directement au nœud par la clé. Par exemple, vous pouvez dire .attributes["dir"]qui est le même que .attributes.getNamedItem('dir');(Note latérale, NamedNodeMapne respecte pas la casse, vous pouvez donc également passer 'DIR');

Il y a une fonction similaire directement dans HTMLElementlaquelle vous pouvez simplement appeler setAttributequi créera automatiquement un nœud s'il n'existe pas et définira le nodeValue. Il existe également certains attributs auxquels vous pouvez accéder directement en tant que propriétés HTMLElementvia des propriétés spéciales , telles que dir. Voici une cartographie approximative de ce à quoi il ressemble:

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 || ''

}

Vous pouvez donc modifier les dirattributs de 6 façons:

  // 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';

Vous pouvez mettre à jour toutes les propriétés avec des méthodes # 1-5, mais seulement dir, id, langet classNameavec la méthode # 6.

Extensions de HTMLElement

HTMLElementa ces 4 propriétés spéciales. Certains éléments sont des classes étendues de HTMLElementpropriétés encore plus mappées. Par exemple, HTMLAnchorElementa HTMLAnchorElement.href, HTMLAnchorElement.relet HTMLAnchorElement.target. Mais attention , si vous définissez ces propriétés sur des éléments qui n'ont pas ces propriétés spéciales (comme sur a HTMLTableElement), les attributs ne sont pas modifiés et ce sont juste des propriétés personnalisées normales. Pour mieux comprendre, voici un exemple de son héritage:

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

Propriétés personnalisées

Maintenant, le gros avertissement: comme tous les objets Javascript , vous pouvez ajouter des propriétés personnalisées. Mais cela ne changera rien sur le DOM. Tu peux faire:

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

Mais c'est la même chose que

  newElement.myCustomDisplayAttribute = 'block';

Cela signifie que l'ajout d'une propriété personnalisée ne sera pas lié à.attributes[attr].nodeValue .

Performance

J'ai construit un cas de test jsperf pour montrer la différence: https://jsperf.com/set-attribute-comparison . Fondamentalement, dans l'ordre:

  1. Propriétés personnalisées car elles n'affectent pas le DOM et ne sont pas des attributs .
  2. Applications spéciales fournies par le navigateur ( dir, id, className).
  3. Si des attributs existent déjà ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Si des attributs existent déjà ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Conclusion (TL; DR)

  • Utilisez les correspondances de propriétés spéciales de HTMLElement: element.dir, element.id, element.classNameou element.lang.

  • Si vous êtes sûr à 100% que l'élément est un étendu HTMLElementavec une propriété spéciale, utilisez ce mappage spécial. (Vous pouvez vérifier avec if (element instanceof HTMLAnchorElement)).

  • Si vous êtes sûr à 100% que l'attribut existe déjà, utilisez element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Sinon, utilisez setAttribute().

Mèche courte
la source
Vous avez mentionné ces quatre mappages de propriétés: dir, id, className et lang. Et classList? La classe class est-elle un mappage de propriété qui est garanti d'exister?
Barzee
classListest garanti à 100%, mais ce n'est pas une propriété de chaîne, c'est un DOMTokenListobjet vivant . La définition .classNamedirecte est plus rapide que la manipulation classList, mais vous écraseriez le tout.
ShortFuse
Que diriez-vous de .value sur les balises <input> et <textarea>? De quel genre sont-ils?
Daniel Williams
Ceux mentionnés dans la réponse sont ce que le W3C appelle "refléter les attributs IDL". Lorsque vous modifiez .value, vous modifiez la valeur interne de la HTMLInputElement, qui se reflète ensuite sur les attributs. Ils ne doivent pas non plus l'être string. .valueAsNumberchangera en value interne , et sa stringforme apparaîtra dans l' valueattribut. developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse
3

"Quand utiliser setAttribute vs .attribute = en JavaScript?"

Une règle générale consiste à utiliser .attribute et de vérifier si cela fonctionne sur le navigateur.

..Si cela fonctionne sur le navigateur, vous êtes prêt à partir.

..Si ce n'est pas le cas, utilisez .setAttribute(attribute, value)plutôt que.attribute pour cet attribut.

Rincer-répéter pour tous les attributs.

Eh bien, si vous êtes paresseux, vous pouvez simplement utiliser .setAttribute. Cela devrait fonctionner correctement sur la plupart des navigateurs. (Bien que les navigateurs qui le prennent en charge .attributepuissent l'optimiser mieux que .setAttribute(attribute, value).)

Pacerier
la source
0

Cela ressemble à un cas où il est préférable d'utiliser setAttribute:

Dev.Opera - JavaScript efficace

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
la source
2
Merci d'avoir partagé ce tomo7, pouvez-vous s'il vous plaît expliquer un peu plus. Ne posElem.style = newStylefonctionne pas dans tous les navigateurs (a fonctionné pour moi dans Firefox)? Est-ce uniquement pour des raisons de performances qui setAttributesont privilégiées, en évitant les repeints? Est-ce posElem.style.cssText = newStyleplus perfomant alors posElem.style = newStyle?
Noitidart
0

méthodes pour définir des attributs (par exemple classe) sur un élément: 1. el.className = string 2. el.setAttribute ('class', string) 3. el.attributes.setNamedItem (object) 4. el.setAttributeNode (node)

J'ai fait un test de référence simple ( ici )

et il semble que setAttributeNode soit environ 3 fois plus rapide que setAttribute.

donc si les performances sont un problème - utilisez "setAttributeNode"

Yair Levy
la source
Je pense que vous exécutez le test sur Chrome. J'ai testé sur mon mac en utilisant Chrome, Safari et Firefox; on s'attend à ce que 3 d'entre eux aient montré 3 résultats différents.
Olgun Kaya
0

Plats à emporter intéressants à partir du script Google API concernant ceci:

Ils le font comme ceci:

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

Remarquez comment ils utilisent setAttributepour "src" et "nonce", mais ensuite .async = ...pour l'attribut "async".

Je ne suis pas sûr à 100%, mais c'est probablement parce que "async" n'est pris en charge que sur les navigateurs qui prennent en charge l' .attr =attribution directe . Donc, cela n'a aucun sens d'essayer sestAttribute("async")parce que si le navigateur ne comprend pas.async=... - il ne comprendra pas l'attribut "async".

J'espère que c'est un aperçu utile de mon projet de recherche "Un-minify GAPI" en cours . Corrige moi si je me trompe.

Maxim Mazurok
la source