Когда использовать setAttribute vs .attribute = в JavaScript?

234

Разработан ли передовой опыт использования нотации атрибута setAttributedot ( .)?

Например:

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

или

myObj.className = "nameOfClass";
myObj.id = "someID";
Франциск
источник
1
Когда я переключился с .setAttribute()на [key] = value, все волшебным образом начало работать.
Андрей

Ответы:

73

Вы всегда должны использовать прямую .attributeформу (но смотрите ссылку на причудливый режим ниже), если вам нужен программный доступ в JavaScript. Он должен правильно обрабатывать различные типы атрибутов (подумайте «onload»).

Используйте getAttribute/, setAttributeкогда вы хотите иметь дело с DOM как есть (например, только текст). Различные браузеры путают их. См. Причудливые режимы: атрибут (в) совместимости .


источник
127
Этот ответ недостаточно ясен ... Я пока не чувствую, что понимаю это.
temporary_user_name
1
@ Aerovistae - согласен с вами в этом. Добавлен новый ответ, который, надеюсь, будет понятнее.
Олан
1
Но если вы хотите повлиять на innerHTML элемента, вы должны использовать 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
Olan
источник
2
и, кроме того, он появляется после того, как последний setAttribute в вашем примере node.frameborderНЕ определен, поэтому вы должны получить getAttribute, чтобы получить значение обратно.
Майкл
5
@ Майкл правильный - если вы используете setAttribute для установки значения, вы должны использовать getAttribute для его получения.
Олан
3
Нет ничего плохого в настройке frameBorderнапрямую, но обратите внимание на заглавные буквы. Кто-то подумал, что было бы неплохо, если бы CamelCase соответствовал JavaScript-атрибутам HTML. Мне не удалось найти никакой спецификации для этого, но сеть, похоже, согласна с тем, что речь идет о 12 конкретных случаях (по крайней мере, для HTML 4). Смотрите, например, следующий пост: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa
1
usemapАтрибут не может быть установлен с помощью точечной нотации при создании карты динамически для изображения. Это требует, img.setAttribute('usemap', "#MapName");подразумевает ли ваш ответ, что usemapпоэтому является "нестандартным"?
Мейферт
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>

Предостережение: это объяснение того, как спецификация 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"]
Фарид Алнамрути
@fareednamrouti Это не работает. Я только что проверил это. Свойства JS не влияют на атрибуты html. Вам действительно нужно установить setAttribute здесь.
Сурьма
@Antimony Это странно, но да, вы на 100% правы, я буду голосовать
Фарид Алнамрути
2
Вы уверены, что нет ariaLabel?
jgmjgm
8

Эти ответы на самом деле не решают большую путаницу между свойствами и атрибутами . Кроме того, в зависимости от прототипа Javascript, иногда вы можете использовать свойство элемента для доступа к атрибутам, а иногда нет.

Во-первых, вы должны помнить, что HTMLElementэто объект Javascript. Как и все объекты, они имеют свойства. Конечно, вы можете создать свойство, называемое почти всем, что вы хотите внутри HTMLElement, но оно не должно иметь ничего общего с DOM (что на странице). Точечная запись ( .) предназначена для свойств . Теперь есть некоторые специальные свойства , которые сопоставляются с атрибутами, и на момент написания этой статьи гарантируется только 4 (подробнее об этом позже).

Все HTMLElementвключают свойство называется attributes. HTMLElement.attributesявляется живым NamedNodeMap объектом, который относится к элементам в DOM. «Живой» означает, что когда узел изменяется в DOM, он изменяется на стороне JavaScript, и наоборот. Атрибутами DOM в данном случае являются рассматриваемые узлы. А Nodeимеет .nodeValueсвойство, которое вы можете изменить. NamedNodeMapОбъекты имеют функцию, которая называется, setNamedItemгде вы можете изменить весь узел. Вы также можете напрямую получить доступ к узлу с помощью ключа. Например, вы можете сказать, .attributes["dir"]что то же самое, что .attributes.getNamedItem('dir');(примечание, 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-comparison . В основном, по порядку:

  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? Является ли classList отображением свойств, которое гарантированно существует?
Барзи
classListна 100% гарантированно существует, но это не строковое свойство, это живой DOMTokenListобъект. Установка .classNameнапрямую быстрее, чем манипулирование classList, но вы перезаписали бы все это.
ShortFuse
Как насчет .value на тегах <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).)

Pacerier
источник
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);
}
tomo7
источник
2
Спасибо, что поделились этим tomo7, пожалуйста, объясните немного больше. Не posElem.style = newStyleработает во всех браузерах (у меня работало в Firefox)? Это просто из соображений производительности, что setAttributeпредпочтительнее, избегая перекрашивания? Является ли posElem.style.cssText = newStyleболее духовным, чем posElem.style = newStyle?
Noitidart
0

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

Я сделал простой тест ( здесь )

и кажется, что setAttributeNode примерно в 3 раза быстрее, чем использование setAttribute.

поэтому, если производительность является проблемой - используйте "setAttributeNode"

Яир Леви
источник
Я думаю, что вы запускаете тест на хром. Я проверил на своем Mac, используя Chrome, Safari и Firefox; как ожидается, 3 из них показали 3 разных результата.
Олгун Кая
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%, но, вероятно, это потому, что «асинхронность» поддерживается только в браузерах, которые поддерживают прямое .attr =назначение. Таким образом, нет смысла пытаться это сделать, sestAttribute("async")потому что если браузер не понимает .async=...- он не поймет атрибут «асинхронный».

Надеюсь, что это полезное понимание моего продолжающегося исследовательского проекта "Un-minify GAPI" . Поправьте меня если я ошибаюсь.

Максим Мазурок
источник