Quando usar setAttribute vs .attribute = em JavaScript?

234

Tem uma prática recomendada em usar setAttribute lugar do ponto (. notação de atributo )?

Por exemplo:

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

ou

myObj.className = "nameOfClass";
myObj.id = "someID";
Francisc
fonte
1
Quando mudei de .setAttribute()para [key] = value, tudo começou a funcionar magicamente.
22419 Andrew Andrew

Respostas:

73

Você deve sempre usar o direto .attribute formulário (mas consulte o link quirksmode abaixo) se desejar acesso programático em JavaScript. Ele deve lidar com os diferentes tipos de atributos (pense em "onload") corretamente.

Use getAttribute/ setAttributequando desejar lidar com o DOM como ele é (por exemplo, apenas texto literal). Navegadores diferentes confundem os dois. Consulte Modos Quirks: compatibilidade de atributos (in) .


fonte
127
Essa resposta não é clara o suficiente ... Eu realmente ainda não entendo isso.
temporary_user_name
1
@ Aerovistae - concordo com você sobre isso. Adicionada uma nova resposta, que é esperançosamente mais clara.
quer
1
Mas se você quiser afetar o innerHTML do elemento, você tem que usar setAttribute ...
Michael
3
Você quer dizer outterHTML * :)
megawac
4
Eu descobri que a.href retorna o URL completo, mas getAttribute ('href') retorna exatamente o que nesse atributo (<a href = "/ help" ...).
Plastic Rabbit
144

Do Javascript: O Guia Definitivo , esclarece as coisas. Ele observa que HTMLElement objetos de um documento HTML definem propriedades JS que correspondem a todos os atributos HTML padrão.

Portanto, você só precisa usar setAttributeatributos não padrão.

Exemplo:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
olan
fonte
2
e além disso, ele aparece após o último setAttribute no seu exemplo, node.frameborderNÃO está definido, portanto você deve getAttribute para recuperar o valor.
Michael Michael
5
@ Michael correto - se você usar setAttribute para definir um valor, deverá usar getAttribute para recuperá-lo.
olan
3
Não há nada errado em definir frameBorderdiretamente, mas observe a capitalização. Alguém pensou que era uma boa idéia camelCase os equivalentes JavaScript dos atributos HTML. Não consegui encontrar nenhuma especificação para isso, mas a rede parece concordar que se trata de 12 casos específicos (pelo menos para o HTML 4). Veja, por exemplo, o seguinte post: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaaa
1
O usemapatributo não pode ser definido usando a notação de ponto ao criar o mapa dinamicamente para uma imagem. Requer img.setAttribute('usemap', "#MapName");Sua resposta implica que usemapé, portanto, "fora do padrão"?
Mseifert 17/02/16
1
Isso está errado. Alguns atributos possuem propriedades definidas, portanto não. É realmente apenas como eles escreveram as especificações. Não tem nada a ver com os atributos serem padrão ou não. No entanto, é verdade que propriedades não padrão só podem ser acessadas com getAttribute ().
Ben Ben
79

Nenhuma das respostas anteriores está completa e a maioria contém informações erradas.

Existem três maneiras de acessar os atributos de um DOM elemento em JavaScript. Todos os três funcionam de maneira confiável nos navegadores modernos, desde que você saiba como utilizá-los.

1 element.attributes

Os elementos têm atributos de propriedade que retornam um NamedNodeMap de objetos Attr ativos . Os índices desta coleção podem ser diferentes entre os navegadores. Portanto, o pedido não é garantido. NamedNodeMappossui métodos para adicionar e remover atributos ( getNamedItemesetNamedItem , respectivamente).

Observe que, embora o XML seja explicitamente sensível a maiúsculas e minúsculas, a especificação do DOM solicita que os nomes de cadeias sejam normalizados ; portanto, os nomes passados getNamedItemsão efetivamente sem distinção entre maiúsculas e minúsculas.

Exemplo 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

Esses métodos existem diretamente no Elementsem a necessidade de acessar attributese seus métodos, mas executam as mesmas funções.

Novamente, observe que o nome da string não diferencia maiúsculas de minúsculas.

Exemplo 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. Propriedades no objeto DOM, como element.id

Muitos atributos podem ser acessados ​​usando propriedades convenientes no objeto DOM. Quais atributos existem dependem do tipo do nó DOM, não quais atributos são definidos no HTML. As propriedades são definidas em algum lugar da cadeia de protótipos do objeto DOM em questão. As propriedades específicas definidas dependerão do tipo de elemento que você está acessando. Por exemplo, classNamee idsão definidos Elemente existem em todos os nós do DOM que são elementos ( por exemplo, não nós de texto ou comentário). Mas valueé mais estreito. Está definido HTMLInputElemente pode não existir em outros elementos.

Observe que as propriedades do JavaScript diferenciam maiúsculas de minúsculas. Embora a maioria das propriedades use letras minúsculas, algumas são camelCase. Portanto, sempre verifique as especificações para ter certeza.

Este "gráfico" captura uma parte da cadeia de protótipos para esses objetos DOM. Não está nem perto de terminar, mas captura a estrutura geral.

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

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

Advertência: Esta é uma explicação de como a especificação HTML define e os navegadores modernos lidam com atributos. Não tentei lidar com as limitações de navegadores antigos e quebrados. Se você precisar oferecer suporte a navegadores antigos, além dessas informações, precisará saber o que está quebrado nesses navegadores.

Ben
fonte
Obrigado por esclarecer isso. Estou curioso, quais versões do IE são consideradas 'modernas' e seguem as especificações HTML?
Jkdev 27/06/16
3
@jkdev O IE nunca fica moderno. O que sempre fica velho.
precisa
Obrigado por uma resposta tão detalhada, eu li muito sobre DOM e herança, como HTMLElement, herdado de Element e assim por diante, sua resposta faz todo sentido.
Suraj Jain
16

Um caso que descobri onde setAttributeé necessário é ao alterar os atributos do ARIA, pois não há propriedades correspondentes. Por exemplo

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

Não há x.arialabel nada disso, então você precisa usar o setAttribute.

Edit: x ["aria-label"] não funciona . Você realmente precisa 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"
Antimônio
fonte
na verdade não é realmente em Javascript que você pode fazer isso x [ "aria-label"]
Fareed Alnamrouti
@fareednamrouti Isso não funciona. Eu apenas testei. As propriedades JS não afetam os atributos html. Você realmente precisa de setAttribute aqui.
Antimony
@Antimony Isso é estranho, mas sim você está 100% certo eu vou votar até
Fareed Alnamrouti
2
Tem certeza de que não existe ariaLabel?
Jgmjgm 19/0318
8

Essas respostas não estão realmente abordando a grande confusão entre propriedades e atributos . Além disso, dependendo do protótipo Javascript, às vezes você pode usar a propriedade de um elemento para acessar um atributo e às vezes não.

Primeiro, você deve se lembrar que an HTMLElementé um objeto Javascript. Como todos os objetos, eles têm propriedades. Claro, você pode criar uma propriedade chamada quase tudo o que deseja dentro HTMLElement, mas não precisa fazer nada com o DOM (o que está na página). A notação de ponto ( .) é para propriedades . Agora, existem algumas propriedades especiais que são mapeadas para atributos e, no momento ou por escrito, existem apenas 4 garantidas (mais sobre isso posteriormente).

Todos os HTMLElements incluem uma propriedade chamada attributes. HTMLElement.attributesé uma vivo NamedNodeMap objecto que se relaciona com os elementos no DOM. "Ao vivo" significa que quando o nó muda no DOM, eles mudam no lado do JavaScript e vice-versa. Os atributos DOM, nesse caso, são os nós em questão. A Nodepossui uma .nodeValuepropriedade que você pode alterar. NamedNodeMapobjetos têm uma função chamada setNamedItemonde você pode alterar o nó inteiro. Você também pode acessar diretamente o nó pela chave. Por exemplo, você pode dizer .attributes["dir"]qual é o mesmo que .attributes.getNamedItem('dir');(Nota lateral, não NamedNodeMapdiferencia maiúsculas de minúsculas, para que você também possa passar 'DIR');

Existe uma função semelhante diretamente na HTMLElementqual você pode simplesmente chamar, setAttributeque criará automaticamente um nó se ele não existir e definirá o nodeValue. Também existem alguns atributos aos quais você pode acessar diretamente como propriedades por HTMLElementmeio de propriedades especiais , como dir. Aqui está um mapeamento aproximado da aparência:

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

}

Então você pode alterar os diratributos de 6 maneiras:

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

Você pode atualizar todas as propriedades com métodos # 1-5, mas apenas dir, id, lang, e classNamecom o método # 6.

Extensões de HTMLElement

HTMLElementtem essas 4 propriedades especiais. Alguns elementos são classes estendidas de HTMLElementpropriedades ainda mais mapeadas. Por exemplo, HTMLAnchorElementtem HTMLAnchorElement.href, HTMLAnchorElement.rele HTMLAnchorElement.target. Mas, cuidado , se você definir essas propriedades em elementos que não possuem essas propriedades especiais (como em a HTMLTableElement), os atributos não serão alterados e serão apenas propriedades personalizadas normais. Para entender melhor, aqui está um exemplo de sua herança:

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

Propriedades personalizadas

Agora, o grande aviso: como todos os objetos Javascript , você pode adicionar propriedades personalizadas. Mas isso não muda nada no DOM. Você pode fazer:

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

Mas é o mesmo que

  newElement.myCustomDisplayAttribute = 'block';

Isso significa que a adição de uma propriedade customizada não será vinculada.attributes[attr].nodeValue .

atuação

Criei um caso de teste jsperf para mostrar a diferença: https://jsperf.com/set-attribute-comparison . Basicamente, em ordem:

  1. Propriedades personalizadas porque elas não afetam o DOM e não são atributos .
  2. Mapeamentos especiais fornecidos pelo navegador ( dir, id, className).
  3. Se já existirem atributos ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Se já existirem atributos ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Conclusão (TL; DR)

  • Use os mapeamentos de propriedades especiais de HTMLElement: element.dir, element.id, element.className, ou element.lang.

  • Se você tiver 100% de certeza de que o elemento é estendido HTMLElementcom uma propriedade especial, use esse mapeamento especial. (Você pode verificar com if (element instanceof HTMLAnchorElement)).

  • Se você tem 100% de certeza de que o atributo já existe, use element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Caso contrário, use setAttribute().

Pavio curto
fonte
Você mencionou esses quatro mapeamentos de propriedades: dir, id, className e lang. E o classList? ClassList é um mapeamento de propriedade que é garantido que existe?
Barzee
classListé 100% garantido que existe, mas não é uma propriedade de string, é um DOMTokenListobjeto ativo. Definir .classNamediretamente é mais rápido do que manipular classList, mas você substituirá a coisa toda.
ShortFuse
Que tal .value nas tags <input> e <textarea>? Que tipo eles são?
Daniel Williams
Os mencionados na resposta são o que o W3C chama de "refletindo atributos do IDL". Quando você altera .value, altera o valor interno de HTMLInputElement, que é refletido nos atributos. Eles também não precisam ser string. .valueAsNumbermudará value internamente e sua stringforma aparecerá no valueatributo developer.mozilla.org/pt-BR/docs/Web/HTML/Attributes
ShortFuse
3

"Quando usar setAttribute vs .attribute = em JavaScript?"

Uma regra geral é usar .attributee verificar se funciona no navegador.

..Se funcionar no navegador, você está pronto para ir.

..Se não, use em .setAttribute(attribute, value)vez de .attributepara esse atributo.

Repita a lavagem para todos os atributos.

Bem, se você é preguiçoso, pode simplesmente usar .setAttribute. Isso deve funcionar bem na maioria dos navegadores. (Embora os navegadores compatíveis .attributepossam otimizá-lo melhor que .setAttribute(attribute, value).)

Pacerier
fonte
0

Parece um caso em que é melhor 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
fonte
2
Obrigado por compartilhar este tomo7, você pode explicar um pouco mais. Não posElem.style = newStylefunciona em todos os navegadores (funcionou para mim no Firefox)? É apenas por razões de desempenho que setAttributeé preferido, evitando os repain? É posElem.style.cssText = newStylemais perfomrant então posElem.style = newStyle?
Noitidart
0

métodos para definir atributos (por exemplo, classe) em um elemento: 1. el.className = string 2. el.setAttribute ('class', string) 3. el.attributes.setNamedItem (objeto) 4. el.setAttributeNode (nó)

Fiz um teste simples de benchmark ( aqui )

e parece que setAttributeNode é cerca de três vezes mais rápido que o setAttribute.

portanto, se o desempenho for um problema - use "setAttributeNode"

Yair Levy
fonte
Eu acho que você executa o teste no chrome. Eu escrevi no meu mac usando chrome, safari e firefox; Espera-se que 3 deles tenham apresentado 3 resultados diferentes.
amigos estão dizendo sobre
0

Publicação interessante do script da API do Google sobre isso:

Eles fazem assim:

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

Observe como eles usam setAttributepara "src" e "nonce", mas depois .async = ...para o atributo "async".

Não tenho 100% de certeza, mas provavelmente é porque o "assíncrono" é suportado apenas em navegadores que suportam .attr =atribuição direta . Portanto, não faz sentido tentar, sestAttribute("async")porque se o navegador não entender.async=... - ele não entenderá o atributo "async".

Felizmente, esse é um insight útil do meu projeto de pesquisa em andamento "Desminificar GAPI" . Corrija-me se eu estiver errado.

Maxim Mazurok
fonte