Kapan menggunakan setAttribute vs .attribute = dalam JavaScript?

234

Sudahkah praktik terbaik seputar penggunaan setAttributealih-alih .notasi atribut dot ( ) telah dikembangkan?

Misalnya:

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

atau

myObj.className = "nameOfClass";
myObj.id = "someID";
Francisc
sumber
1
Ketika saya beralih dari .setAttribute()ke [key] = value, semuanya mulai bekerja secara ajaib.
Andrew

Jawaban:

73

Anda harus selalu menggunakan direct .attribute formulir (tetapi lihat tautan quirksmode di bawah) jika Anda ingin akses terprogram dalam JavaScript. Itu harus menangani berbagai jenis atribut (pikirkan "membebani") dengan benar.

Gunakan getAttribute/ setAttributeketika Anda ingin berurusan dengan DOM sebagaimana adanya (mis. Hanya teks literal). Browser yang berbeda membingungkan keduanya. Lihat mode Quirks: kompatibilitas atribut (dalam) .


sumber
127
Jawaban ini tidak cukup jelas ... Saya belum merasa mengerti ini.
temporary_user_name
1
@Aerovistae - setuju dengan Anda tentang ini. Menambahkan jawaban baru yang diharapkan lebih jelas.
Olan
1
Tetapi jika Anda ingin memengaruhi innerHTML elemen, Anda harus menggunakan setAttribute ...
Michael
3
Maksud Anda outterHTML * :)
megawac
4
Saya menemukan bahwa a.href mengembalikan url penuh, tetapi getAttribute ('href') mengembalikan apa yang ada di atribut itu (<a href = "/ help" ...).
Kelinci Plastik
144

Dari Javascript: The Definitive Guide , ini menjelaskan berbagai hal. Ini mencatat bahwa objek HTMLElement dari dokumen HTML mendefinisikan properti JS yang sesuai dengan semua atribut HTML standar.

Jadi, Anda hanya perlu menggunakan setAttributeatribut non-standar.

Contoh:

node.className = 'test'; // works
node.frameborder = '0'; // doesn't work - non standard attribute
node.setAttribute('frameborder', '0'); // works
olan
sumber
2
dan lebih jauh lagi, ini muncul setelah setAttribute terakhir dalam contoh Anda, node.frameborderTIDAK didefinisikan, jadi Anda harus mendapatkanAttribute untuk mendapatkan kembali nilainya.
Michael
5
@Michael benar - jika Anda menggunakan setAttribute untuk menetapkan nilai, Anda harus menggunakan getAttribute untuk mengambilnya.
Olan
3
Tidak ada yang salah dengan pengaturan frameBordersecara langsung, tetapi perhatikan kapitalisasi. Seseorang berpikir itu adalah ide yang sangat bagus untuk menambahkan persamaan JavaScript pada atribut HTML. Saya belum berhasil menemukan spesifikasi untuk ini, tetapi internet tampaknya setuju bahwa ini adalah masalah 12 kasus tertentu (setidaknya untuk HTML 4). Lihat misalnya posting berikut: drupal.org/node/1420706#comment-6423420
aaaaaaaaaaaa
1
The usemapatribut tidak dapat diatur menggunakan notasi dot saat membuat peta dinamis untuk gambar. Membutuhkan img.setAttribute('usemap', "#MapName");Apakah jawaban Anda menyiratkan bahwa usemapoleh karena itu "tidak standar"?
mseifert
1
Ini kebanyakan salah. Beberapa atribut memiliki properti yang ditentukan, jadi jangan. Ini benar-benar hanya tentang bagaimana mereka menulis spec. Ini tidak ada kaitannya dengan atribut yang standar atau tidak. Namun, memang benar bahwa tidak ada properti standar hanya dapat diakses dengan getAttribute ().
Ben
79

Tidak ada jawaban sebelumnya yang lengkap dan sebagian besar berisi informasi yang salah.

Ada tiga cara mengakses atribut Elemen DOM dalam JavaScript. Ketiganya bekerja dengan andal di peramban modern selama Anda mengerti cara menggunakannya.

1. element.attributes

Elemen memiliki atribut properti yang mengembalikan NamedNodeMap langsung dari objek Attr . Indeks koleksi ini mungkin berbeda di antara peramban. Jadi, pesanannya tidak dijamin. NamedNodeMapmemiliki metode untuk menambah dan menghapus atribut ( getNamedItemdan setNamedItem, masing-masing).

Perhatikan bahwa meskipun XML secara eksplisit peka terhadap huruf besar-kecil, spesifikasi DOM meminta nama string untuk dinormalisasi , sehingga nama yang diteruskan menjadi getNamedItemtidak sensitif huruf besar-kecil.

Contoh penggunaan:

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

Metode ini ada langsung di Elementtanpa perlu mengakses attributesdan metodenya tetapi melakukan fungsi yang sama.

Sekali lagi, perhatikan bahwa nama string tidak sensitif huruf.

Contoh penggunaan:

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. Properti pada objek DOM, seperti element.id

Banyak atribut dapat diakses menggunakan properti yang mudah digunakan pada objek DOM. Atribut mana yang ada tergantung pada jenis simpul DOM, bukan atribut mana yang didefinisikan dalam HTML. Properti didefinisikan di suatu tempat di rantai prototipe objek DOM yang bersangkutan. Properti spesifik yang ditentukan akan tergantung pada jenis Elemen yang Anda akses. Misalnya, classNamedan iddidefinisikan Elementdan ada pada semua node DOM yang merupakan elemen (mis. Bukan teks atau komentar). Tapivalue lebih sempit. Ini didefinisikan HTMLInputElementdan mungkin tidak ada pada elemen lain.

Perhatikan bahwa properti JavaScript peka huruf besar-kecil. Meskipun sebagian besar properti akan menggunakan huruf kecil, beberapa camelCase. Jadi selalu periksa spek untuk memastikan.

"Bagan" ini menangkap sebagian rantai prototipe untuk objek DOM ini. Bahkan tidak hampir selesai, tetapi ia menangkap keseluruhan struktur.

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

Contoh penggunaan:

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>

Peringatan: Ini adalah penjelasan tentang bagaimana spesifikasi HTML mendefinisikan dan browser modern menangani atribut. Saya tidak mencoba untuk berurusan dengan batasan dari browser yang rusak dan kuno. Jika Anda perlu mendukung browser lama, selain informasi ini, Anda perlu tahu apa yang rusak di browser tersebut.

Ben
sumber
Terima kasih sudah membereskannya. Saya ingin tahu, versi IE mana yang dianggap 'modern' dan mengikuti spesifikasi HTML?
jkdev
3
@ jkdev IE tidak pernah menjadi modern. Apa pun yang menjadi tua.
Suraj Jain
Terima kasih atas jawaban terperinci seperti itu, saya membaca banyak tentang DOM dan warisan seperti HTMLElement mewarisi dari Elemen dan seterusnya, jawaban Anda masuk akal.
Suraj Jain
16

Satu kasus yang saya temukan di mana setAttributediperlukan adalah ketika mengubah atribut ARIA, karena tidak ada properti yang sesuai. Sebagai contoh

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

Tidak ada x.arialabel atau semacamnya, jadi Anda harus menggunakan setAttribute.

Sunting: x ["label aria"] tidak berfungsi . Anda benar-benar membutuhkan 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"
Antimon
sumber
sebenarnya tidak terlalu dalam Javascript Anda bisa melakukan ini x ["aria-label"]
Fareed Alnamrouti
@fareednamrouti Itu tidak berhasil. Saya baru saja mengujinya. Properti JS tidak memengaruhi atribut html. Anda benar-benar membutuhkan setAttribute di sini.
Antimony
@Antimony Ini aneh tapi Ya, Anda 100% benar saya akan memilih
Fareed Alnamrouti
2
Apakah Anda yakin tidak ada ariaLabel?
jgmjgm
8

Jawaban ini tidak benar-benar mengatasi kebingungan besar antara properti dan atribut . Juga, tergantung pada prototipe Javascript, terkadang Anda dapat menggunakan properti elemen untuk mengakses atribut dan terkadang Anda tidak bisa.

Pertama, Anda harus ingat bahwa objek HTMLElementadalah Javascript. Seperti semua objek, mereka memiliki properti. Tentu, Anda dapat membuat properti yang disebut hampir semua yang Anda inginkan di dalamnya HTMLElement, tetapi tidak harus melakukan apa pun dengan DOM (apa yang ada di halaman). Notasi titik ( .) adalah untuk properti . Sekarang, ada beberapa properti khusus yang dipetakan ke atribut, dan pada saat itu atau tulisan hanya ada 4 yang dijamin (lebih lanjut tentang itu nanti).

Semua HTMLElementtermasuk properti yang disebut attributes. HTMLElement.attributesadalah Obyek hidup NamedNodeMap yang berkaitan dengan unsur-unsur dalam DOM. "Langsung" berarti bahwa ketika node berubah di DOM, mereka berubah di sisi JavaScript, dan sebaliknya. Atribut DOM, dalam hal ini, adalah simpul yang dimaksud. A Nodememiliki .nodeValueproperti yang dapat Anda ubah. NamedNodeMapobjek memiliki fungsi yang disebut di setNamedItemmana Anda dapat mengubah seluruh node. Anda juga dapat langsung mengakses simpul dengan kunci. Misalnya, Anda dapat mengatakan .attributes["dir"]yang sama dengan .attributes.getNamedItem('dir');(Catatan samping, NamedNodeMaptidak peka huruf besar-kecil, sehingga Anda juga dapat lulus 'DIR');

Ada fungsi serupa langsung di HTMLElementmana Anda bisa memanggil setAttributeyang secara otomatis akan membuat simpul jika tidak ada dan mengatur nodeValue. Ada juga beberapa atribut yang dapat Anda akses langsung sebagai properti HTMLElementmelalui properti khusus , seperti dir. Berikut ini pemetaan kasar dari tampilannya:

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

}

Jadi, Anda dapat mengubah diratribut 6 cara:

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

Anda dapat memperbarui semua properti dengan metode # 1-5, tetapi hanya dir, id, lang, dan classNamedengan metode # 6.

Ekstensi HTMLElement

HTMLElementmemiliki 4 sifat khusus tersebut. Beberapa elemen adalah kelas yang diperluas yang HTMLElementmemiliki lebih banyak properti yang dipetakan. Misalnya, HTMLAnchorElementmemiliki HTMLAnchorElement.href, HTMLAnchorElement.rel, dan HTMLAnchorElement.target. Namun, berhati-hatilah , jika Anda menyetel properti tersebut pada elemen yang tidak memiliki properti khusus itu (seperti pada a HTMLTableElement) maka atribut tidak berubah dan itu hanya, properti kustom normal. Untuk lebih memahami, inilah contoh warisannya:

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

Properti Khusus

Sekarang peringatan besar: Seperti semua objek Javascript , Anda dapat menambahkan properti khusus. Tapi, itu tidak akan mengubah apa pun pada DOM. Anda dapat melakukan:

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

Tapi itu sama dengan

  newElement.myCustomDisplayAttribute = 'block';

Ini berarti bahwa menambahkan properti khusus tidak akan ditautkan ke.attributes[attr].nodeValue .

Performa

Saya telah membangun test case jsperf untuk menunjukkan perbedaannya: https://jsperf.com/set-attribute-comparison . Pada dasarnya, Agar:

  1. Properti khusus karena tidak memengaruhi DOM dan bukan atribut .
  2. Pemetaan khusus yang disediakan oleh browser ( dir, id, className).
  3. Jika atribut sudah ada ,element.attributes.ATTRIBUTENAME.nodeValue =
  4. setAttribute ();
  5. Jika atribut sudah ada ,element.attributes.getNamedItem(ATTRIBUTENAME).nodeValue = newValue
  6. element.attributes.ATTRIBUTENAME = newNode
  7. element.attributes.setNamedItem(ATTRIBUTENAME) = newNode

Kesimpulan (TL; DR)

  • Gunakan pemetaan properti khusus dari HTMLElement: element.dir,element.id , element.className, atau element.lang.

  • Jika Anda 100% yakin elemennya adalah extended HTMLElement dengan properti khusus, gunakan pemetaan khusus itu. (Anda dapat memeriksanya if (element instanceof HTMLAnchorElement)).

  • Jika Anda 100% yakin atributnya sudah ada, gunakan element.attributes.ATTRIBUTENAME.nodeValue = newValue.

  • Jika tidak, gunakan setAttribute().

ShortFuse
sumber
Anda menyebutkan empat pemetaan properti ini: dir, id, className, dan lang. Bagaimana dengan classList? Apakah classList pemetaan properti yang dijamin ada?
Barzee
classListdijamin 100% ada, tapi ini bukan properti string, ini DOMTokenListobjek langsung . Pengaturan .classNamesecara langsung lebih cepat daripada memanipulasi classList, tetapi Anda akan menimpa semuanya.
ShortFuse
Bagaimana dengan .value pada tag <input> dan <textarea>? Jenis apa mereka?
Daniel Williams
Yang disebutkan dalam jawaban adalah apa yang disebut W3C "mencerminkan atribut IDL". Bila Anda mengubah .value, Anda mengubah internal yang nilai HTMLInputElement, yang kemudian tercermin pada atribut. Mereka juga tidak harus seperti itu string. .valueAsNumberakan berubah secara value internal , dan stringformulir itu akan muncul di valueatribut. developer.mozilla.org/en-US/docs/Web/HTML/Attributes
ShortFuse
3

"Kapan menggunakan setAttribute vs .attribute = dalam JavaScript?"

Aturan umum adalah untuk menggunakan .attributedan memeriksa apakah itu berfungsi di browser.

..Jika itu bekerja di browser, Anda baik untuk pergi.

..Jika tidak, gunakan .setAttribute(attribute, value)bukan .attributeuntuk yang atribut.

Bilas-ulangi untuk semua atribut.

Nah, jika Anda malas Anda cukup menggunakan .setAttribute. Itu seharusnya bekerja dengan baik pada sebagian besar browser. (Meskipun browser yang mendukung .attributedapat mengoptimalkannya lebih baik daripada .setAttribute(attribute, value).)

Pacerier
sumber
0

Ini terlihat seperti satu kasus di mana lebih baik menggunakan setAttribute:

Dev.Opera - JavaScript yang Efisien

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
sumber
2
Terima kasih telah berbagi tomo7 ini, tolong jelaskan sedikit lebih banyak. Tidak posElem.style = newStyleberfungsi di semua browser (berfungsi untuk saya di Firefox)? Apakah hanya karena alasan kinerja yang setAttributelebih disukai, menghindari pengecatan ulang? Apakah posElem.style.cssText = newStylelebih banyak perfomran posElem.style = newStyle?
Noitidart
0

metode untuk menetapkan atribut (misalnya kelas) pada elemen: 1. el.className = string 2. el.setAttribute ('class', string) 3. el.attributes.setNamedItem (objek) 4. el.setAttributeNode (node)

Saya telah membuat tes benchmark sederhana ( di sini )

dan tampaknya setAttributeNode sekitar 3 kali lebih cepat daripada menggunakan setAttribute.

jadi jika kinerja merupakan masalah - gunakan "setAttributeNode"

Yair Levy
sumber
Saya pikir Anda menjalankan tes pada chrome. Saya menempelkan mac saya menggunakan chrome, safari dan firefox; diharapkan 3 dari mereka menunjukkan 3 hasil yang berbeda.
Olgun Kaya
0

Pengambilan yang menarik dari skrip Google API mengenai hal ini:

Mereka melakukannya seperti ini:

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

Perhatikan, bagaimana mereka digunakan setAttributeuntuk "src" dan "nonce", tapi kemudian.async = ... atribut untuk atribut "async".

Saya tidak 100% yakin, tapi mungkin itu karena "async" hanya didukung di browser yang mendukung .attr =penugasan langsung . Jadi, tidak masuk akal mencoba sestAttribute("async")karena jika browser tidak mengerti.async=... - itu tidak akan mengerti atribut "async".

Semoga itu adalah wawasan bermanfaat dari proyek penelitian "Un-minify GAPI" saya yang sedang berlangsung . Koreksi saya jika saya salah.

Maxim Mazurok
sumber