Kiedy używać setAttribute vs .attribute = w JavaScript?

234

Czy opracowano najlepszą praktykę dotyczącą używania setAttributezamiast .notacji atrybutu dot ( )?

Na przykład:

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

lub

myObj.className = "nameOfClass";
myObj.id = "someID";
Franciszek
źródło
1
Kiedy przeszedłem od .setAttribute()celu [key] = value, wszystko zaczęło się w magiczny sposób działa.
Andrew

Odpowiedzi:

73

Powinieneś zawsze używać bezpośredniego .attributeformularza (ale zobacz link quirksmode poniżej), jeśli chcesz mieć programowy dostęp w JavaScript. Powinien poprawnie obsługiwać różne typy atrybutów (myśl „onload”).

Użyj getAttribute/, setAttributejeśli chcesz poradzić sobie z DOM takim, jakim jest (np. Tylko dosłowny tekst). Różne przeglądarki mylą te dwa. Zobacz Tryby dziwactwa: zgodność atrybutów (nie) .


źródło
127
Ta odpowiedź nie jest wystarczająco jasna ... Naprawdę nie wydaje mi się, że jeszcze to rozumiem.
tymczasowego
1
@Aerovistae - zgadzam się z tym w tej sprawie. Dodano nową odpowiedź, która, mam nadzieję, jest jaśniejsza.
olan
1
Ale jeśli chcesz wpłynąć na innerHTML elementu, musisz użyć setAttribute ...
Michael
3
Masz na myśli outterHTML * :)
megawac
4
Odkryłem, że a.href zwraca pełny adres URL, ale getAttribute ('href') zwraca dokładnie to, co w tym atrybucie (<a href = "/ help" ...).
Plastikowy królik
144

Od Javascript: The Definitive Guide , wyjaśnia wszystko. Zauważa, że obiekty HTMLElement dokumentu HTML definiują właściwości JS, które odpowiadają wszystkim standardowym atrybutom HTML.

Musisz więc użyć tylko setAttributew przypadku niestandardowych atrybutów.

Przykład:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
olan
źródło
2
a ponadto pojawia się po tym, jak ostatni setAttribute w twoim przykładzie node.frameborderNIE jest zdefiniowany, więc musisz uzyskaćAtrybut, aby odzyskać wartość.
Michael
5
@ Michael poprawnie - jeśli używasz setAttribute do ustawienia wartości, musisz użyć getAttribute, aby ją odzyskać.
olan
3
Nie ma nic złego w ustawianiu frameBorderbezpośrednio, ale zwróć uwagę na wielkie litery. Ktoś pomyślał, że to dobry pomysł na wielbłądzieZamów JavaScript w odpowiednikach atrybutów HTML. Nie udało mi się znaleźć żadnej specyfikacji tego, ale wydaje się, że sieć zgadza się, że jest to kwestia 12 konkretnych przypadków (przynajmniej dla HTML 4). Zobacz na przykład następujący post: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa
1
usemapAtrybut nie można ustawić za pomocą notacji kropki przy tworzeniu mapy dynamicznie na obrazie. Wymaga img.setAttribute('usemap', "#MapName");Czy Twoja odpowiedź sugeruje, że usemapjest to zatem „niestandardowe”?
mseifert
1
To w większości złe. Niektóre atrybuty mają zdefiniowane właściwości, więc nie. Tak naprawdę chodzi o to, jak napisali specyfikację. Nie ma z tego powodu, ponieważ atrybuty są standardowe, czy nie. Prawdą jest jednak, że do niestandardowych właściwości można uzyskać dostęp tylko za pomocą getAttribute ().
Ben
79

Żadna z poprzednich odpowiedzi nie jest kompletna, a większość zawiera błędne informacje.

Istnieją trzy sposoby uzyskania dostępu do atrybutów elementu DOM w JavaScript. Wszystkie trzy działają niezawodnie w nowoczesnych przeglądarkach, o ile wiesz, jak z nich korzystać.

1. element.attributes

Elementy mają właściwość atrybuty , które zwraca na żywo NamedNodeMap z Attr obiektów. Indeksy tej kolekcji mogą się różnić w zależności od przeglądarki. Zatem zamówienie nie jest gwarantowane. NamedNodeMapma metody dodawania i usuwania atrybutów ( getNamedItemi setNamedItemodpowiednio).

Zwróć uwagę, że chociaż w XML rozróżniana jest wielkość liter, specyfikacja DOM wymaga znormalizowania nazw ciągów , więc w nazwach przekazywanych getNamedItemfaktycznie nie jest rozróżniana wielkość liter.

Przykładowe użycie:

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.getAttributeielement.setAttribute

Te metody istnieją bezpośrednio, Elementbez potrzeby uzyskiwania dostępu attributesi ich metod, ale wykonują te same funkcje.

Ponownie zauważ, że w nazwach łańcuchów nie rozróżniana jest wielkość liter.

Przykładowe użycie:

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. Właściwości obiektu DOM, takie jak element.id

Dostęp do wielu atrybutów można uzyskać za pomocą wygodnych właściwości obiektu DOM. To, które atrybuty istnieją, zależy od typu węzła DOM, a nie które atrybuty są zdefiniowane w kodzie HTML. Właściwości są zdefiniowane gdzieś w łańcuchu prototypowym przedmiotowego obiektu DOM. Określone określone właściwości będą zależeć od typu elementu, do którego uzyskujesz dostęp. Na przykład, classNamei idsą zdefiniowane Elementi istnieje na wszystkich węzłach DOM, które elementy (tj. Nie tekstowe lub komentarz węzły). Ale valuejest bardziej wąski. Jest zdefiniowany HTMLInputElementi może nie istnieć w innych elementach.

Zauważ, że we właściwościach JavaScript rozróżniana jest wielkość liter. Chociaż większość właściwości będzie używać małych liter, niektóre z nich to camelCase. Dlatego zawsze należy sprawdzić specyfikację, aby się upewnić.

Ten „wykres” przechwytuje część łańcucha prototypów dla tych obiektów DOM. Nie jest nawet blisko ukończenia, ale oddaje ogólną strukturę.

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

Przykładowe użycie:

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>

Zastrzeżenie: jest to wyjaśnienie, w jaki sposób określa specyfikację HTML, a nowoczesne przeglądarki obsługują atrybuty. Nie próbowałem radzić sobie z ograniczeniami starożytnych, zepsutych przeglądarek. Jeśli potrzebujesz obsługiwać stare przeglądarki, oprócz tych informacji musisz wiedzieć, co jest zepsute w tych przeglądarkach.

Ben
źródło
Dzięki za wyjaśnienie tego. Jestem ciekawy, które wersje IE są uważane za „nowoczesne” i zgodne ze specyfikacją HTML?
jkdev
3
@jkdev IE nigdy nie staje się nowoczesny. Cokolwiek może się zestarzeć.
Suraj Jain,
Dzięki za tak szczegółową odpowiedź, dużo czytałem o DOM i dziedziczeniu, takim jak dziedziczenie HTMLElement z elementu i tak dalej, twoja odpowiedź ma doskonały sens.
Suraj Jain,
16

Jeden przypadek, który uznałem za setAttributeniezbędny, to zmiana atrybutów ARIA, ponieważ nie ma odpowiednich właściwości. Na przykład

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

Nie ma x.arialabelnic takiego, więc musisz użyć setAttribute.

Edycja: x [„aria-label”] nie działa . Naprawdę potrzebujesz 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"
Antymon
źródło
właściwie nie tak naprawdę w Javascript, możesz to zrobić x ["aria-label"]
Fareed Alnamrouti
@fareednamrouti To nie działa. Właśnie to przetestowałem. Właściwości JS nie wpływają na atrybuty HTML. Naprawdę potrzebujesz setAttribute tutaj.
Antymon
@Antimony To dziwne, ale tak, masz 100% racji zagłosuję
Fareed Alnamrouti
2
Czy na pewno nie ma ariaLabel?
jgmjgm
8

Te odpowiedzi nie są odpowiedzią na duże zamieszanie między właściwościami a atrybutami . Ponadto, w zależności od prototypu Javascript, czasami można użyć właściwości elementu, aby uzyskać dostęp do atrybutów, a czasem nie.

Najpierw musisz pamiętać, że an HTMLElementjest obiektem Javascript. Podobnie jak wszystkie obiekty mają właściwości. Jasne, możesz utworzyć właściwość o nazwie prawie wszystko, co chcesz w środku HTMLElement, ale nie musi ona nic robić z DOM (co jest na stronie). Notacja kropkowa ( .) dotyczy właściwości . Teraz istnieją pewne specjalne właściwości, które są mapowane na atrybuty, a w tym czasie lub podczas pisania są tylko 4, które są gwarantowane (więcej na ten temat później).

Wszystkie HTMLElements zawierają właściwość o nazwie attributes. HTMLElement.attributesto obiekt na żywo, NamedNodeMap który odnosi się do elementów w DOM. „Na żywo” oznacza, że ​​gdy węzeł zmienia się w DOM, zmieniają się po stronie JavaScript i odwrotnie. W tym przypadku atrybutami DOM są omawiane węzły. A Nodema .nodeValuewłaściwość, którą możesz zmienić. NamedNodeMapobiekty mają funkcję o nazwie, w setNamedItemktórej można zmienić cały węzeł. Możesz także uzyskać bezpośredni dostęp do węzła za pomocą klucza. Na przykład możesz powiedzieć, .attributes["dir"]który jest taki sam jak .attributes.getNamedItem('dir');(Uwaga boczna, NamedNodeMapnie rozróżnia wielkości liter, więc możesz również przekazać 'DIR');

Istnieje podobna funkcja bezpośrednio, w HTMLElementktórej możesz po prostu wywołać, setAttributektóra automatycznie utworzy węzeł, jeśli nie istnieje i ustawi nodeValue. Istnieją również niektóre atrybuty, do których można uzyskać bezpośredni dostęp jako właściwości HTMLElementza pośrednictwem specjalnych właściwości , takich jak dir. Oto przybliżone odwzorowanie tego, jak to wygląda:

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

}

Możesz więc zmienić diratrybuty na 6 sposobów:

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

Można zaktualizować wszystkie właściwości z metodami # 1-5, ale tylko dir, id, lang, i classNamemetodą # 6.

Rozszerzenia HTMLElement

HTMLElementma te 4 specjalne właściwości. Niektóre elementy są rozszerzonymi klasami o HTMLElementjeszcze większej liczbie odwzorowanych właściwości. Na przykład, HTMLAnchorElementma HTMLAnchorElement.href, HTMLAnchorElement.reli HTMLAnchorElement.target. Ale uwaga : jeśli ustawisz te właściwości na elementach, które nie mają tych specjalnych właściwości (jak na a HTMLTableElement), wówczas atrybuty nie zostaną zmienione i będą to zwykłe niestandardowe właściwości. Aby lepiej zrozumieć, oto przykład jego dziedziczenia:

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

Właściwości niestandardowe

Teraz wielkie ostrzeżenie: Jak wszystkie obiekty JavaScript , możesz dodawać własne właściwości. Ale to nic nie zmieni w DOM. Możesz to zrobić:

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

Ale to to samo co

  newElement.myCustomDisplayAttribute = 'block';

Oznacza to, że dodanie niestandardowej właściwości nie zostanie połączone.attributes[attr].nodeValue .

Występ

Zbudowałem skrzynkę testową jsperf, aby pokazać różnicę: https://jsperf.com/set-attribute-comparison . Zasadniczo w celu:

  1. Właściwości niestandardowe, ponieważ nie wpływają na DOM i nieatrybutami .
  2. Specjalne mapowania świadczone przez przeglądarkę ( dir, id, className).
  3. Jeśli atrybuty już istnieją ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Jeśli atrybuty już istnieją ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Wniosek (TL; DR)

  • Za pomocą specjalnych mapowania własności z HTMLElement: element.dir, element.id, element.className, lub element.lang.

  • Jeśli masz 100% pewności, że element jest rozszerzeniem HTMLElemento specjalnej właściwości, użyj tego specjalnego odwzorowania. (Możesz to sprawdzić za pomocą if (element instanceof HTMLAnchorElement)).

  • Jeśli masz 100% pewności, że atrybut już istnieje, użyj element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Jeśli nie, użyj setAttribute().

ShortFuse
źródło
Wspomniałeś o tych czterech mapowaniach właściwości: dir, id, className i lang. Co z listą klas? Czy classList to mapowanie właściwości, które gwarantuje istnienie?
Barzee
classLististnieje 100% gwarancji, że istnieje, ale nie jest to ciąg znaków, to DOMTokenListobiekt na żywo . .classNameBezpośrednie ustawienie jest szybsze niż manipulowanie classList, ale nadpisujesz całość.
ShortFuse
Co powiesz na .value w tagach <input> i <textarea>? Jakiego rodzaju oni są?
Daniel Williams
Te wymienione w odpowiedzi są tym, co W3C nazywa „odzwierciedleniem atrybutów IDL”. Kiedy zmieniasz .value, zmieniasz wartość wewnętrznąHTMLInputElement , która jest następnie odzwierciedlana w atrybutach. Nie muszą tak być string. .valueAsNumberzmieni się value wewnętrznie , a jego stringforma pojawi się w valueatrybucie. developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse
3

„Kiedy używać setAttribute vs .attribute = w JavaScript?”

Ogólną zasadą jest używanie .attributei sprawdzanie, czy działa w przeglądarce.

..Jeśli działa w przeglądarce, możesz zacząć.

..If nie, użyj .setAttribute(attribute, value)zamiast .attributeza które atrybutu.

Powtórz płukanie dla wszystkich atrybutów.

Cóż, jeśli jesteś leniwy, możesz po prostu użyć .setAttribute. To powinno działać dobrze w większości przeglądarek. (Chociaż obsługiwane przeglądarki .attributemogą to zoptymalizować lepiej niż .setAttribute(attribute, value).)

Pacerier
źródło
0

Wygląda to na jeden przypadek, w którym lepiej jest użyć setAttribute:

Dev.Opera - Wydajny JavaScript

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
źródło
2
Dziękujemy za udostępnienie tego tomo7. Proszę wyjaśnić nieco więcej. Nie posElem.style = newStyledziała we wszystkich przeglądarkach (działało dla mnie w przeglądarce Firefox)? Czy jest setAttributeto preferowane ze względów wydajnościowych , unikając malowania? Czy posElem.style.cssText = newStylezatem więcej perfomrantu posElem.style = newStyle?
Noitidart,
0

metody ustawiania atrybutów (na przykład klasy) na elemencie: 1. el.className = string 2. el.setAttribute ('class', string) 3. el.attributes.setNamedItem (object) 4. el.setAttributeNode (node)

Zrobiłem prosty test porównawczy ( tutaj )

i wygląda na to, że setAttributeNode jest około 3 razy szybszy niż przy użyciu setAttribute.

więc jeśli problem stanowi wydajność - użyj „setAttributeNode”

Yair Levy
źródło
Myślę, że uruchamiasz test na Chrome. Tetowałem na moim Macu używając Chrome, Safari i Firefoxa; spodziewane 3 z nich wykazały 3 różne wyniki.
Olgun Kaya
0

Ciekawe wynos ze skryptu API Google temat dotyczące tego:

Robią to w ten sposób:

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

Zauważ, jak używają setAttributedo „src” i „nonce”, ale potem.async = ... dla atrybutu „async”.

Nie jestem w 100% pewien, ale prawdopodobnie dzieje się tak, ponieważ „asynchronizacja” jest obsługiwana tylko w przeglądarkach obsługujących bezpośrednie .attr =przypisanie. Więc nie ma sensu próbować, sestAttribute("async")ponieważ jeśli przeglądarka nie rozumie.async=... - nie zrozumie atrybutu „asynchronicznie”.

Mam nadzieję, że jest to pomocne informacje z mojego bieżącego projektu badawczego „Un-minify GAPI” . Popraw mnie, jeśli się mylę.

Maxim Mazurok
źródło