Коли використовувати setAttribute vs .attribute = в JavaScript?


234

Чи розроблена найкраща практика щодо використання setAttributeзамість .позначення атрибута dot ( )?

Наприклад:

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

або

myObj.className = "nameOfClass";
myObj.id = "someID";

1
Коли я перейшов .setAttribute()на [key] = value, все почало магічно працювати.
Андрій

Відповіді:


73

Ви завжди повинні використовувати прямий .attribute форму (але дивіться посилання quirksmode нижче), якщо хочете програмного доступу в JavaScript. Він повинен правильно обробляти різні типи атрибутів (подумати "onload").

Використовуйте getAttribute/ setAttributeколи ви бажаєте мати справу з DOM таким, яким він є (наприклад, лише дослівний текст). Різні браузери плутають ці два. Див. Режими Quirks: сумісність атрибутів (in) .


127
Ця відповідь недостатньо чітка ... Я ще не відчуваю, що я ще це розумію.
тимчасове_користувач

1
@Aerovistae - згоден з вами з цього приводу. Додано нову відповідь, яка, сподіваємось, зрозуміліша.
олан

1
Але якщо ви хочете вплинути на внутрішнійHTML елемент, вам доведеться використовувати setAttribute ...
Michael

3
Ви маєте на увазі outterHTML * :)
megawac

4
Я виявив, що a.href повертає повну URL-адресу, але getAttribute ('href') повертає саме те, що в цьому атрибуті (<a href = "/ help" ...).
Пластиковий кролик

144

З Javascript: Постійний посібник , він уточнює речі. Він зазначає, що HTMLElement об’єкти документа HTML визначають властивості JS, які відповідають усім стандартним атрибутам HTML.

Тому потрібно використовувати лише setAttributeдля нестандартних атрибутів.

Приклад:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works

2
і, крім того, він з’являється після останнього setAttribute у вашому прикладі, node.frameborderНЕ визначено, тому вам потрібно отриматиAttribute, щоб отримати значення назад.
Майкл

5
@Michael правильний - якщо ви використовуєте setAttribute для встановлення значення, ви повинні використовувати getAttribute для його отримання.
олан

3
Немає нічого поганого в налаштуванні frameBorderбезпосередньо, але зауважте великі літери. Хтось вважав, що це було веселою гарною ідеєю camelCase JavaScript-еквіваленти атрибутів HTML. Мені не вдалося знайти жодної специфікації для цього, але мережа, схоже, погоджується, що мова йде про 12 конкретних випадках (принаймні для HTML 4). Дивіться, наприклад, наступне повідомлення: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa

1
usemapАтрибут не може бути встановлений з допомогою точкової нотації при створенні карти динамічно для зображення. Це вимагає, img.setAttribute('usemap', "#MapName");чи відповідає ваша відповідь, що usemapтому є "нестандартною"?
mseifert

1
Це здебільшого неправильно. У деяких атрибутах визначені властивості, тому не слід. Це дійсно лише про те, як вони написали специфікацію. Це не має нічого спільного з тим, що атрибути є стандартними чи ні. Однак вірно, що до нестандартних властивостей можна отримати доступ лише за допомогою getAttribute ().
Бен

79

Жодна з попередніх відповідей не є повною і більшість містить дезінформацію.

Існує три способи доступу до атрибутів елемента DOM в JavaScript. Усі три працюють надійно в сучасних браузерах, доки ви розумієте, як їх використовувати.

1. element.attributes

Елементи мають властивість атрибутів , який повертає живий NamedNodeMap з Attr об'єктів. Показники цієї колекції можуть бути різними серед браузерів. Отже, порядок не гарантується. NamedNodeMapмає методи додавання та видалення атрибутів ( getNamedItemі setNamedItem, відповідно).

Зауважте, що хоч XML явно чутливий до регістру, специфікація DOM вимагає нормалізації імен рядків , тому імена, передані в getNamedItem, фактично не чутливі до регістру.

Приклад використання:

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

Ці методи існують безпосередньо в тих, що Elementне потребують доступу attributesта методів, але виконують ті самі функції.

Знову зауважте, що назва рядка є нечутливою до регістру.

Приклад використання:

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. Властивості об’єкта DOM, такі як element.id

До багатьох атрибутів можна отримати доступ, використовуючи зручні властивості об’єкта DOM. Які атрибути існують, залежить від типу вузла DOM, а не які атрибути визначені в HTML. Властивості визначені десь у прототипі ланцюга DOM об'єкта, про який йде мова. Визначені конкретні властивості залежатимуть від типу Елементу, до якого ви отримуєте доступ. Наприклад,className і idвизначені на Elementі існує на всіх вузли DOM , які є елементами (тобто. Чи не текст або коментар вузли). Але valueє більш вузьким. Він визначений HTMLInputElementі може не існувати на інших елементах.

Зауважте, що властивості JavaScript залежать від регістру. Хоча більшість властивостей використовуватиме малі літери, деякі - camelCase. Тому завжди перевіряйте специфікацію, щоб бути впевненою.

Ця "діаграма" фіксує частину ланцюга прототипу для цих об'єктів DOM. Він навіть не близький до завершення, але він фіксує загальну структуру.

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

Приклад використання:

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>

Caveat: Це пояснення того, як визначається специфікація HTML і сучасні браузери обробляють атрибути. Я не намагався боротися з обмеженнями давніх, зламаних браузерів. Якщо вам потрібно підтримувати старі браузери, окрім цієї інформації, вам потрібно знати, що порушено в цих браузерах.


Дякуємо, що очистили це. Мені цікаво, які версії IE вважаються "сучасними" та відповідають специфікаціям HTML?
jkdev

3
@jkdev IE ніколи не стає сучасним. Що коли-небудь стає старішим.
Сурай Джайн

Дякую за таку детальну відповідь, я багато читав про DOM та спадщину, як наслідок HTMLElement від Element і так далі, ваша відповідь має ідеальний сенс.
Сурай Джайн

16

Один випадок, який я знайшов там, де setAttributeце необхідно, - це при зміні атрибутів ARIA, оскільки немає відповідних властивостей. Наприклад

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

Немає x.arialabel нічого подібного, тому вам доведеться використовувати setAttribute.

Редагувати: x ["aria-label"] не працює . Вам дійсно потрібен 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"

насправді насправді не в Javascript ви можете зробити це x ["aria-label"]
Fareed Alnamrouti

@fareednamrouti Це не працює. Я просто тестував це. Властивості JS не впливають на атрибути html. Тут вам дійсно потрібен setAttribute.
Сурма

@Antimin Це дивно, але так, ви маєте 100% рацію, я проголосую
Fareed Alnamrouti

2
Ви впевнені, що немає ariaLabel?
jgmjgm

8

Ці відповіді насправді не стосуються великої плутанини між властивостями та атрибутами . Крім того, залежно від прототипу Javascript, іноді ви можете використовувати властивість елемента для доступу до атрибутів, а іноді - неможливо.

По-перше, ви повинні пам’ятати, що ан HTMLElementє об’єктом Javascript. Як і всі об'єкти, вони мають властивості. Звичайно, ви можете створити властивість, яку називають майже все, що ви хочете всередині HTMLElement, але це не має нічого спільного з DOM (що на сторінці). Нотація крапок ( .) призначена для властивостей . Тепер є деякі особливі властивості , які відображаються в атрибутах, а на час або запис є лише 4, які є гарантованими (докладніше про це пізніше).

Усі HTMLElements включають властивість, яку називають attributes. HTMLElement.attributesє живим NamedNodeMap Об'єктом, що стосується елементів у DOM. "Live" означає, що коли вузол змінюється в DOM, вони змінюються на стороні JavaScript, і навпаки. Атрибути DOM, в даному випадку, - це відповідні вузли. A Nodeмає .nodeValueвластивість, яку ви можете змінити. NamedNodeMapоб’єкти мають функцію, яку називають, setNamedItemде можна змінити весь вузол. Ви також можете безпосередньо отримати доступ до вузла клавішею. Наприклад, ви можете сказати, .attributes["dir"]що таке саме, як .attributes.getNamedItem('dir');(Side note, NamedNodeMapнечутлива до регістру, тому ви також можете пройти 'DIR');

Існує подібна функція безпосередньо HTMLElementтам, де ви можете просто зателефонувати, setAttributeяка автоматично створить вузол, якщо його не існує, і встановити nodeValue. Є також деякі атрибути, до яких можна отримати доступ безпосередньо до властивостей за HTMLElementдопомогою спеціальних властивостей , таких як dir. Ось приблизне відображення того, як воно виглядає:

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

}

Таким чином, ви можете змінити dirатрибути 6 способів:

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

Ви можете оновити всі властивості з методами # 1-5, але тільки dir, id, langі classNameз методом # 6.

Розширення HTMLElement

HTMLElementмає ці 4 особливі властивості. Деякі елементи є розширеними класами, які HTMLElementмають ще більш відображені властивості. Наприклад, HTMLAnchorElementє HTMLAnchorElement.href, HTMLAnchorElement.relі HTMLAnchorElement.target. Але будьте обережні , якщо ви встановите ці властивості на елементи, які не мають цих спеціальних властивостей (наприклад, на a HTMLTableElement), то атрибути не змінюються, і вони є просто звичайними спеціальними властивостями. Щоб краще зрозуміти, ось приклад його успадкування:

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

Спеціальні властивості

Тепер велике попередження: Як і всі об'єкти Javascript , ви можете додати власні властивості. Але вони нічого не змінять у DOM. Ви можете зробити:

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

Але це те саме, що

  newElement.myCustomDisplayAttribute = 'block';

Це означає, що додавання власної властивості не буде пов’язано з.attributes[attr].nodeValue .

Продуктивність

Я створив тестовий випадок jsperf, щоб показати різницю: https://jsperf.com/set-attribute-compitation . В основному для того, щоб:

  1. Спеціальні властивості, оскільки вони не впливають на DOM і не є атрибутами .
  2. Спеціальні відображення , що надаються браузером ( dir, id, className).
  3. Якщо атрибути вже є ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Якщо атрибути вже є ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Висновок (TL; DR)

  • Використовуйте спеціальні відображення властивостей з HTMLElement: element.dir, element.id, element.className, або element.lang.

  • Якщо ви на 100% впевнені, що елемент розширений HTMLElementзі спеціальним властивістю, використовуйте це спеціальне відображення. (Ви можете перевірити if (element instanceof HTMLAnchorElement)).

  • Якщо ви впевнені на 100%, атрибут вже існує, використовуйте element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Якщо ні, використовуйте setAttribute().


Ви згадали про ці чотири відображення властивостей: dir, id, className та lang. А що з classList? Чи є mapList відображенням властивостей, яке гарантовано існує?
Barzee

classListна 100% гарантовано існує, але це не властивість рядка, це живий DOMTokenListоб'єкт. Налаштування .classNameбезпосередньо - швидше, ніж маніпулювання classList, але ви перезаписали б усе.
ShortFuse

Як щодо. Значення на тегах <input> та <textarea>? Які вони?
Даніель Вільямс

Зазначені у відповіді - це те, що W3C називає "відображаючими атрибутами IDL". Коли ви змінюєте .value, ви змінюєте внутрішнє значення HTMLInputElement, яке потім відображається на атрибутах. Вони також не повинні бути string. .valueAsNumberзміниться value внутрішньо , і його stringформа з’явиться в valueатрибуті. developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse

3

"Коли використовувати setAttribute vs .attribute = в JavaScript?"

Загальне правило - використовувати .attributeта перевіряти, чи працює він у браузері.

..Якщо це працює в браузері, ви добре підете.

..Якщо він не використовується .setAttribute(attribute, value)замість того , щоб .attributeдля цього атрибута.

Промийте-повторіть для всіх атрибутів.

Ну, якщо ти лінивий, то просто можеш використати .setAttribute. Це має чудово працювати у більшості браузерів. (Хоча веб-переглядачі, які підтримують, .attributeможуть оптимізувати її краще .setAttribute(attribute, value).)


0

Це схоже на один випадок, коли краще використовувати setAttribute:

Dev.Opera - ефективний 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);
}

2
Дякуємо, що поділилися цим tomo7, будь ласка, поясніть трохи більше. Хто posElem.style = newStyleне працює у всіх браузерах (працював для мене в Firefox)? Чи setAttributeє кращим саме з міркувань продуктивності , уникаючи перефарбовування? Є posElem.style.cssText = newStyleще perfomrant тоді posElem.style = newStyle?
Noitidart

0

методи встановлення атрибутів (наприклад, клас) для елемента: 1. el.className = рядок 2. el.setAttribute ('клас', рядок) 3. el.attributes.setNamedItem (об'єкт) 4. el.setAttributeNode (вузол)

Я зробив простий тестовий тест ( тут )

і здається, що setAttributeNode приблизно в 3 рази швидше, ніж використання setAttribute.

тому якщо продуктивність не є проблемою - використовуйте "setAttributeNode"


Думаю, ви запускаєте тест на хром. Я тестував свій Mac, використовуючи хром, сафарі та фаєрфокс; очікувалося, що 3 з них показали 3 різні результати.
Olgun Kaya

0

Цікавий вихід із сценарію Google API щодо цього:

Вони роблять так:

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

Зверніть увагу, як вони використовуються setAttributeдля "src" та "nonce", але потім .async = ...для атрибута "async".

Я не впевнений на 100%, але, мабуть, це тому, що "async" підтримується лише в браузерах, які підтримують пряме .attr =призначення. Отже, немає сенсу намагатися, sestAttribute("async")тому що якщо браузер не зрозуміє .async=...- він не зрозуміє атрибут "async".

Будемо сподіватися, що це корисне розуміння мого поточного дослідницького проекту "Un-minify GAPI" . Виправте мене, якщо я помиляюся.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.