6 คะแนน โดย GN⁺ 2024-04-27 | 3 ความคิดเห็น | แชร์ทาง WhatsApp
  • แม้จะอยู่บนองค์ประกอบเดียวกัน HTML attributes และ DOM properties ก็ทำงานเหมือนพื้นที่จัดเก็บคนละส่วน การเปลี่ยนฝั่งหนึ่งจึงอาจไม่สะท้อนไปอีกฝั่งโดยอัตโนมัติ
  • Attributes จะมองเห็นได้ใน HTML serialization และในแผง Elements ของเครื่องมือสำหรับนักพัฒนา แต่ properties เป็น สถานะเฉพาะของ DOM จึงไม่ปรากฏในผลลัพธ์อย่าง outerHTML
  • ค่า attribute เป็น สตริง เสมอ และชื่อไม่แยกแยะตัวพิมพ์ใหญ่-เล็ก แต่ property สามารถมีได้หลายชนิดข้อมูล เช่น object และชื่อแยกแยะตัวพิมพ์ใหญ่-เล็ก
  • มี properties ที่สะท้อน attributes เช่น id, className, htmlFor, ariaLabel ขณะที่ก็มีข้อยกเว้นที่ สถานะปัจจุบันกับค่าเริ่มต้น ถูกแยกออกจากกัน เช่น input.value
  • แต่ละเฟรมเวิร์กตั้งค่าเป้าหมายต่างกัน โดย Preact และ VueJS มักให้ความสำคัญกับ properties, React มักให้ความสำคัญกับ attributes ส่วน Lit แยกทั้งสองอย่างชัดเจนด้วยไวยากรณ์ .value

Attributes กับ properties อาจต่างกันแม้มีชื่อเดียวกัน

  • Attributes และ properties อาจทำงานเหมือนพื้นที่จัดเก็บคนละส่วน
    • ใน `` คำสั่ง getAttribute('foo') จะคืนค่า 'bar' แต่ div.foo อาจเป็น undefined
    • แม้หลังจากตั้งค่า div.foo = 'hello world' ค่า attribute foo ก็ยังคงเป็น 'bar'
  • แม้ในเทมเพลตของเฟรมเวิร์กจะใช้ไวยากรณ์ที่ดูเหมือน attribute เช่น `` ภายในอาจตั้งค่าเป็น property แล้วแต่กรณี
    • กรณีใดจะตั้งค่าเป็น property นั้นแตกต่างกันไปตามแต่ละเฟรมเวิร์ก
    • การตั้งค่าบางอย่างอาจมีผลข้างเคียงที่เปลี่ยนทั้ง property และ attribute

สิ่งที่เหลืออยู่ใน HTML serialization คือ attributes

  • Attributes มีอยู่ทั้งใน HTML และ DOM แต่ properties มีอยู่เฉพาะใน DOM
  • แม้จะรันทั้ง div.setAttribute('foo', 'bar') และ div.hello = 'world' แล้ว div.outerHTML ก็จะแสดงเฉพาะ attributes เช่น `

`

  • แผง Elements ในเครื่องมือสำหรับนักพัฒนาของเบราว์เซอร์แสดง attributes ขององค์ประกอบ ไม่ได้แสดง properties ทั้งหมด

ชนิดของค่าและการจัดการตัวพิมพ์ใหญ่-เล็ก

  • ค่า attribute ต้องทำงานได้ในรูปแบบที่ serialize แล้ว จึงเป็น สตริง เสมอ
    • หากใส่ object ด้วย setAttribute จะถูกเก็บเป็นสตริง "[object Object]"
    • หากใส่ object เดียวกันลงใน property ก็อาจคงชนิดเป็น object ไว้ได้
  • ชื่อ attribute ไม่แยกแยะตัวพิมพ์ใหญ่-เล็ก
    • HeLlO="world" อาจถูก normalize เป็น attribute hello ใน DOM
    • setAttribute('FOO', 'bar') ก็จะปรากฏเป็น attribute foo
  • ชื่อ property แยกแยะตัวพิมพ์ใหญ่-เล็ก
    • แม้ตั้งค่า div.TeSt = 'value' แล้ว div.test ก็อาจเป็น undefined
  • ต่างจากชื่อ attribute ค่า ของ attribute แยกแยะตัวพิมพ์ใหญ่-เล็ก

Properties ที่สะท้อน attributes

  • Properties บางตัว สะท้อน (reflect) attributes เฉพาะ
    • div.id ทำงานเป็น getter/setter ที่อ่านและเขียน attribute id
    • เมื่อรัน div.id = 'bar' attribute id ก็จะเปลี่ยนเป็น 'bar' ด้วย
  • ในความสัมพันธ์แบบสะท้อน attribute คือแหล่งข้อมูลหลัก
    • เมื่อตั้งค่า property จะอัปเดต attribute
    • เมื่ออ่าน property จะอ่านค่าของ attribute
  • Attributes ที่กำหนดไว้ในสเปกมักมี property คู่กันเพื่อความสะดวก
    • attribute foo แบบกำหนดเองไม่ใช่ attribute ที่กำหนดไว้ในสเปก จึงไม่เกิด property foo ที่ตรงกันโดยอัตโนมัติ
  • สเปก `` นิยาม attributes ไว้ใน “Content attributes” และนิยาม properties ไว้ใน “DOM interface”
    • IDL attributes reversed และ type ต้อง reflect content attributes ชื่อเดียวกัน

ความสัมพันธ์แบบสะท้อนที่ชื่อแตกต่างกัน

  • Properties บางตัวมีชื่อไม่เหมือน attribute ที่สะท้อน
  • บางกรณีเปลี่ยนชื่อเพื่อให้เข้ากับสไตล์ของ JavaScript properties
    • el.crossOrigin ของ `` สะท้อน attribute crossorigin
    • el.ariaLabel ของทุกองค์ประกอบสะท้อน attribute aria-label
    • ARIA reflecting properties ใช้งานได้ข้ามเบราว์เซอร์เมื่อปลายปี 2023 และก่อนหน้านั้นใช้ได้เฉพาะ attributes เท่านั้น
  • บางกรณีเปลี่ยนชื่อเพราะคำสงวนของ JavaScript ในอดีต
    • el.className ของทุกองค์ประกอบสะท้อน attribute class
    • el.htmlFor ของ `` สะท้อน attribute for

การตรวจสอบความถูกต้อง การแปลงชนิด และค่าเริ่มต้น

  • Properties อาจมี การตรวจสอบความถูกต้องและค่าเริ่มต้น แต่ตัว attribute เองไม่มีพฤติกรรมเช่นนั้น
  • input.type แสดงความแตกต่างระหว่าง attribute กับ property ได้ดี
    • แม้ input.getAttribute('type') จะเป็น null แต่ input.type จะคืนค่าเริ่มต้น 'text'
    • input.type = 'number' จะดูเหมือนเป็น 'number' ทั้งใน attribute และ property
    • input.type = 'foo' จะใส่ 'foo' ใน attribute แต่ property getter เห็นว่าเป็นค่าที่ไม่ถูกต้องจึงคืนค่า 'text'
  • Properties บางตัวทำ การแปลงชนิด
    • ใน `` ค่า details.getAttribute('open') เป็นสตริงว่าง และ details.open เป็น true
    • details.open = false จะลบ attribute open
    • details.open = 'hello' ไม่ได้ใส่สตริงลงใน attribute ตรง ๆ แต่แปลงเป็น boolean ทำให้ attribute open อยู่ในสถานะที่มีอยู่
  • Properties อย่าง img.height จะแปลงค่า attribute เป็นตัวเลข
    • setter จะแปลงค่าที่รับเข้ามาเป็นตัวเลข และจัดการค่าติดลบเป็น 0

input.value แยกค่าปัจจุบันออกจากค่าเริ่มต้น

  • ใน input มีทั้ง property value และ attribute value แต่ property value ไม่ได้สะท้อน attribute value
  • Property ที่สะท้อน attribute value คือ defaultValue
    • getter ของ defaultValue คืนค่า attribute value หรือสตริงว่าง
    • setter ของ defaultValue ตั้งค่า attribute value เป็นสตริง
  • ตอนแรก property value จะตาม defaultValue แต่เมื่อถูกตั้งค่าด้วย JavaScript หรือการโต้ตอบของผู้ใช้แล้ว จะใช้ค่าภายใน
    • ใน `` เริ่มต้น value, defaultValue, และ getAttribute('value') ล้วนเป็น 'default'
    • เมื่อรัน input.defaultValue = 'new default' attribute และ value จะดูเหมือนเป็น 'new default' ไปด้วยกัน
    • หลังจากนั้นเมื่อรัน input.value = 'hello!' ค่า value จะกลายเป็น 'hello!' แต่ attribute และ defaultValue จะยังคงเป็น 'new default'
    • แม้รัน setAttribute('value', 'another new default') ค่า value ปัจจุบันก็ยังคงเป็น 'hello!'
  • เมื่อรีเซ็ตฟอร์ม อาจทำงานโดยล้างสถานะ value ภายในกลับไป

Attributes เหมาะกับการตั้งค่า ส่วน properties เหมาะกับสถานะมากกว่า

  • การแบ่งว่า attributes ใช้กับ การตั้งค่า (configuration) และ properties ใช้กับ สถานะ (state) เป็นแนวทางที่เป็นธรรมชาติ
  • `` เป็นตัวอย่างที่เข้ากับการแบ่งนี้ ยกเว้นเรื่องชื่อ
    • attribute value ตั้งค่าเริ่มต้น
    • property value ให้สถานะปัจจุบัน
  • การที่การตรวจสอบความถูกต้องถูกใช้เมื่ออ่านหรือเขียน property แต่ไม่ถูกใช้เมื่ออ่านหรือเขียน attribute ก็เข้ากับแนวทางนี้
  • และ แสดงสถานะเปิดด้วย attribute open และเบราว์เซอร์เพิ่มหรือลบ attribute นี้เองโดยตรงตามการโต้ตอบของผู้ใช้
    • การออกแบบนี้ทำลายการแบ่งว่า attributes มีไว้เพื่อการตั้งค่า
    • เฟรมเวิร์กที่ดูแล DOM หรือโค้ด JavaScript ทั่วไปต้องเตรียมรับสถานการณ์ที่ DOM เปลี่ยนตัวเอง
  • ทางเลือกในการออกแบบอาจแบ่งเป็น attribute สำหรับการตั้งค่าเริ่มต้นอย่าง `` property details.open สำหรับอ่านและเขียนสถานะปัจจุบัน และ CSS pseudo-class สำหรับเล็งเป้าสถานะ
  • Simon Peters ค้นพบ การอภิปรายการออกแบบในช่วงแรก ที่เกี่ยวข้องกับเรื่องนี้
  • contenteditable ก็ทำลายการแบ่งนี้เช่นกัน แต่ถือเป็น opt-in ที่ยอมรับความแตกหลายอย่าง

วิธีจัดการของแต่ละเฟรมเวิร์ก

  • Preact และ VueJS จะตั้งค่าเป็น property เมื่อ propName in element เป็นจริง ยกเว้นข้อยกเว้นที่กำหนดไว้ล่วงหน้าบางรายการ มิฉะนั้นจะตั้งค่าเป็น attribute
    • โดยพื้นฐานแล้วชอบ properties มากกว่า attributes
    • เมธอดเรนเดอร์เป็นสตริงทำงานตรงกันข้าม และจะละเว้นรายการที่เป็น property-only
    • โค้ดที่เกี่ยวข้อง: setProperty ของ Preact, shouldSetAsProp ของ VueJS
  • React จะตั้งค่าเป็น attribute ยกเว้นข้อยกเว้นที่กำหนดไว้ล่วงหน้าบางรายการ
    • วิธีนี้ทำให้เกิดลอจิกที่คล้ายกับเมธอดเรนเดอร์เป็นสตริง
    • Properties ของ custom elements ไม่อยู่ในรายการที่ React กำหนดไว้ล่วงหน้า จึงถูกตั้งค่าเป็น attributes และ API ที่เป็น property-only อาจไม่ทำงาน
    • ใน React 19 มีแผนจะเปลี่ยนไปใช้โมเดลแบบ Preact/VueJS สำหรับ custom elements
    • React ทำให้ชื่อ property className เป็นที่นิยมในไวยากรณ์ที่ดูเหมือน attribute แต่ภายในตั้งค่า class attribute
    • โค้ดที่เกี่ยวข้อง: setProp ของ React
  • lit-html รักษาความแตกต่างระหว่าง attribute กับ property ไว้ในไวยากรณ์
    • หากเติม . หน้าชื่อ เช่น `` จะตั้งค่าเป็น property ไม่ใช่ attribute
    • เอกสารที่เกี่ยวข้อง: Lit expressions

3 ความคิดเห็น

 
GN⁺ 2024-04-27
ความคิดเห็นจาก Hacker News
  • การใช้ แอตทริบิวต์ data-* ถือเป็นทางประนีประนอมที่ดีระหว่างแนวทางต่าง ๆ ที่กล่าวถึงในบทความ
    แอตทริบิวต์เหล่านี้จะถูกเปิดเผยโดยอัตโนมัติใน JavaScript เป็นพร็อพเพอร์ตีแบบอ่าน/เขียนของ dataset จึงเข้าถึงได้เช่น document.getElementById('myDiv').dataset.payload == "something"
    แต่ต้องระวังว่าแอตทริบิวต์ HTML ใช้ kebab-case ส่วน JavaScript ใช้ camelCase ดังนั้น data-my-cool-data-attribute จะกลายเป็น dataset.myCoolDataAttribute

    • ไม่ชอบ การแปลงรูปแบบตัวพิมพ์ใหญ่อัตโนมัติ แบบนี้จริง ๆ
      ในหลาย ecosystem ที่ใช้ camelCase ยังไม่มีธรรมเนียมที่ตกลงกันชัดเจนว่าจะจัดการตัวย่ออักษรใหญ่ยังไง ทำให้กรณีอย่าง dataset.myID คลุมเครือ
    • เพื่อหลีกเลี่ยงการแปลงระหว่าง kebab-case กับ camelCase จึงใช้เมธอดอย่าง Element.hasAttribute('data-thing'), Element.getAttribute('data-thing'), Element.setAttribute('data-thing', '...')
      เวลาตรวจสอบหรือดีบักใน DOM ไม่ต้องผ่านการแปลงที่ดูฝืน ๆ ทำให้ เจตนาชัดเจนกว่ามาก
    • เอกสาร data attributes ของ MDN ครอบคลุมไวยากรณ์ HTML, การเข้าถึงจาก JavaScript, การเข้าถึงจาก CSS และข้อควรระวังด้าน accessibility: https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Us...
      อีกทั้งที่ https://developer.mozilla.org/en-US/docs/Web/HTML/Global_att... ยังอธิบายแอตทริบิวต์ทั่วโลก data-* ว่าเป็น “แอตทริบิวต์ข้อมูลแบบกำหนดเองที่ทำให้สคริปต์แลกเปลี่ยนข้อมูลของตัวเองระหว่าง HTML กับการแทนค่าใน DOM ได้”
    • แอตทริบิวต์เหล่านี้สามารถ เข้าถึงได้จาก CSS ในระดับหนึ่งด้วย
    • ยังเป็นอีกวิธีที่น่าสนใจในการสื่อสารระหว่าง execution context ของ JavaScript
      context ที่พูดถึงนี้ไม่ได้หมายถึง iframe ที่ต่างกันเสมอไป แต่หมายถึงกรณีที่สร้าง execution context ของ JavaScript แยกต่างหากที่เข้าถึง DOM เดียวกันได้ เช่น browser extension
      ในศัพท์ของ DevTools Protocol บางทีก็เรียกสิ่งนี้ว่า isolated worlds
  • สิ่งที่บทความพูดถึงแสดงให้เห็นความซับซ้อนและพฤติกรรมที่ไม่ได้ช่วยชีวิตอะไรมากนัก
    พฤติกรรมแบบนี้มีแต่ข้อเสียไม่มีข้อดี และสเปกควรกำหนดให้วัตถุ DOM กับวัตถุ JavaScript เป็นสิ่งเดียวกันไปเลย ทำให้แอตทริบิวต์กับพร็อพเพอร์ตีเป็นแนวคิดเดียวกันด้วย

    • ถ้ามีข้อจำกัดแบบนั้น “ง่าย ๆ” จริง เฟรมเวิร์ก UI สมัยใหม่ส่วนใหญ่คงทำงานไม่ได้เลย
      การเก็บ ค่าที่ไม่ใช่สตริง ไว้ใน DOM node ได้เป็นข้อดีที่ชัดเจน และไม่ใช่เรื่องที่ควรมองข้ามง่าย ๆ
    • ถ้าตัดคำว่า “ง่าย ๆ” ออกก็เห็นด้วย
      มันไม่ง่ายเลย เว้นแต่จะบังคับให้พร็อพเพอร์ตีทั้งหมดเป็นได้แค่สตริง
      ยังต้องคำนึงถึงการแปลงกับตัวเลข พร็อพเพอร์ตีที่ตั้งค่าเป็นอ็อบเจกต์ได้ และพร็อพเพอร์ตีที่ไม่มีแอตทริบิวต์คู่กันด้วย
      ถึงอย่างนั้น ถ้ามีธรรมเนียมหรือกฎที่ตายตัว 100% ก็คงดีกว่า แต่เรื่องนั้นไม่ใช่เรื่องง่าย
    • ถ้าทำแบบนั้น อินเทอร์เฟซ DOM จะใส่ ค่าที่ไม่ใช่สตริง ไม่ได้ และยังมีเมธอดรวมอยู่ด้วย ดังนั้นความสามารถส่วนใหญ่ของ DOM จะหายไป
    • การทำให้แอตทริบิวต์กับพร็อพเพอร์ตีสมมาตรกันเป็นเรื่องยากใน DOM API เดิม
      เมธอดอย่าง addEventListener เป็นพร็อพเพอร์ตีบน prototype ของ DOM element แต่ถ้ามองเป็นแอตทริบิวต์สตริงก็ไม่สมเหตุสมผล
      ถ้าอย่างนั้นเมธอดของ DOM element คงต้องกลายเป็น free function อย่าง HTMLElement.addEventListener(foo, 'some-event', e => console.log(e)); ซึ่งแม้จะเป็นเรื่องรสนิยม แต่ใน JavaScript การเรียกแบบ เมธอดของอ็อบเจกต์ ได้ก็ดีกว่า
    • ไม่ค่อยเข้าใจว่าทำไมทั้งสองอย่างต้องต่างกัน
      DOM element มีอินเทอร์เฟซที่เฉพาะเจาะจงมากสำหรับเปลี่ยนสถานะด้วย setAttribute อยู่แล้ว และก็สงสัยว่าทำไมเมื่อไปแปะพร็อพเพอร์ตีลงบนอ็อบเจกต์ JavaScript ที่สร้างมาเพื่อส่งข้อความไปยัง DOM ผ่านอินเทอร์เฟซนั้น จึงควรคาดหวังว่ามันจะถูก route ให้อัตโนมัติ
      API แบบนั้นอาจให้ประสบการณ์ที่ดีกว่าก็ได้ แต่มีโจทย์ยากอยู่หลายอย่าง และถึงอย่างไรก็ดูเหมือนจะสะอาดกว่าจริง ๆ
      อาจเป็นเพราะเขียนโค้ดแบ็กเอนด์มานานเกินไป เวลาเห็นอินเทอร์เฟซแบบนี้เลยรู้สึกว่าข้างในมีเรื่องแปลก ๆ เยอะ และเหมือนนักพัฒนาผู้ใจดีในอดีตให้ wrapper ที่ปลอดภัยที่สุดเท่าที่จะทำได้มาแล้ว
  • มุมมองที่ช่วยให้เข้าใจคือ แอตทริบิวต์ในความหมายแคบคือ DOM node นั่นคือ node ชนิด Attr
    node นี้สามารถมีพร็อพเพอร์ตีอย่าง name, value, ownerElement, prefix ได้ และโดยรวมจะเข้าถึงได้ในรูป NamedNodeMap ที่ไม่ใช่อาร์เรย์ เป็นค่าของพร็อพเพอร์ตี attributes ของ element เจ้าของ
    ดังนั้นชื่อ getAttribute(name) จึงอาจทำให้เข้าใจผิด และจริง ๆ แล้วมันไม่ได้คืน node แอตทริบิวต์เอง แต่คืนพร็อพเพอร์ตี value ของ node แอตทริบิวต์นั้น จึงใกล้เคียงกับ getAttributeValue(name) มากกว่า
    อ้างอิง: https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Element/get..., https://developer.mozilla.org/en-US/docs/Web/API/Attr

    • คำอธิบายนั้นถูกต้อง แต่ไม่แน่ใจว่ามีอะไรที่ใช้งานจริงที่ทำด้วย setAttribute, getAttribute, getAttributeNames, hasAttribute ไม่ได้ และทำได้เฉพาะด้วย node แอตทริบิวต์ หรือ el.attributes เท่านั้นหรือเปล่า
      จะย้าย node แอตทริบิวต์ระหว่าง element ก็ไม่ได้ และจะเปลี่ยนลำดับ node แอตทริบิวต์ภายใน element ก็ไม่ได้
  • Jake แล้วแบบนี้ล่ะจะทำยังไง: https://jakearchibald.com/2023/against-self-closing-tags-in-...

    • “คุณจะเห็นไวยากรณ์นี้ในบล็อกของผม เพราะ Prettier ทำให้เป็นแบบนั้น และผมก็ชอบ Prettier มากจริง ๆ”
  • เข้าใจตรรกะที่ว่าไม่ควรอัปเดตพร็อพเพอร์ตี แต่การที่สามารถกำหนดเป้าหมายด้วย CSS ได้นั้นมีประโยชน์

    • ดังนั้นจึงบอกว่าควรมี pseudo-class คล้ายกับ :checked ขององค์ประกอบอินพุต
    • สำหรับผม กรณีนี้สำคัญกว่าเหตุผลอื่น ๆ ทั้งหมด
      การตรวจดู DOM ทันทีนั้น ดีบักได้ง่ายกว่ามาก เมื่อเทียบกับการต้องผ่านดีบักเกอร์
  • บางทีความแตกต่างที่สำคัญที่สุดคือ ความคงอยู่ แต่บทความไม่ได้พูดถึง
    attribute จะคงอยู่ตราบใดที่ยังเข้าถึงอ็อบเจ็กต์ root เดียวกันของ DOM tree นั้นได้ เช่น ยังไม่ออกจากหน้า
    object property จะคงอยู่แค่จนกว่าอ็อบเจ็กต์นั้นจะถูก garbage collect

    • อ็อบเจ็กต์มีอยู่ตราบเท่าที่ DOM node ที่มี attribute นั้นยังมีอยู่ ดังนั้นวงจรชีวิตจึงเหมือนกัน
      หาก DOM node ถูก clone หรือผ่านการ serialize/deserialize ก็จะกลายเป็น DOM node อีกตัวหนึ่งที่มี attribute เดียวกัน แต่จะไม่มี property ที่ไม่ใช่ค่าเริ่มต้นของ node อื่น
    • ในที่นี้ไม่มี ความแตกต่างของพฤติกรรมจริง นอกจากความแตกต่างเดิมระหว่าง attribute กับ property
    • อยากรู้ว่าความแตกต่างนั้นปรากฏให้เห็นจริงอย่างไร
  • เป็นบทความที่น่าสนใจ
    มององค์ประกอบ DOM ใน JavaScript เป็นเหมือน proxy มาโดยตลอด
    มันทำให้เราทำงานกับองค์ประกอบ DOM จริงได้ แต่ในขณะเดียวกันก็เป็นแค่อ็อบเจ็กต์ JavaScript ดังนั้นนอกจากการใช้ฟังก์ชันที่โต้ตอบกับองค์ประกอบจริงแล้ว ยังรู้สึกว่าสามารถกำหนดค่าและทำสิ่งที่ต้องการได้ด้วย

  • อธิบายได้ดีพร้อมตัวอย่าง อ่านแล้วดี โดยเฉพาะมีรายละเอียดหลายอย่างที่มักพลาดกันได้ง่าย จึงน่าจะเป็นประโยชน์กับหลายคน

  • DOM property เรียกอีกอย่างว่า IDL attributes

    • ถูกต้อง แต่จงใจเลี่ยงคำนั้น
      นอกกลุ่มผู้เขียนสเปกแล้ว ไม่ค่อยมีใครเรียกว่า IDL attributes และถ้าเรียกทั้งสองอย่างว่า attribute จะสับสนมาก
  • เป็นบทความที่ดี และทำให้อยากรู้ว่ารูปแบบการแสดงออกที่ดีกว่านี้จะหน้าตาเป็นอย่างไร
    ถ้า HTML สามารถมี attribute ที่ไม่ใช่สตริงได้ และค่านั้นอ้างอิงค่าท้องถิ่นของแท็ก script ที่มี scope อยู่ ก็น่าจะน่าสนใจ
    มุมมองที่เห็น attribute เป็นการตั้งค่าเริ่มต้นนั้นค่อนข้างแปลก
    เช่น ถ้า attribute class แสดงแค่ค่าเริ่มต้น เวลาสลับ class ก็จะเกิดเรื่องค่อนข้างแปลก เช่น DOM inspector หรือ HTML ที่แปลงเป็นสตริงแล้วไม่แสดง class ที่กำลัง active อยู่ หรือกลับแสดง class ที่ inactive อยู่

    • ไม่ได้บอกว่าเป็นการตั้งค่าเริ่มต้น แต่เป็นแค่ การตั้งค่า
      attribute class ทำงานได้ดีในระบบนี้
      ในฐานะเจ้าของเอกสาร ผมตั้งค่า attribute class และเมื่อเวลาผ่านไปก็อาจเปลี่ยนการตั้งค่านั้นได้ แต่เป็นสิ่งที่ผมทำเองเท่านั้น
      ถ้าเป็นระบบทางเลือก เบราว์เซอร์คงเพิ่มหรือลบ class เองเพื่อตอบสนองต่อสถานะ hover หรือ focus แต่โชคดีที่มันไม่ทำแบบนั้น และใช้ pseudo-class แทน
 
superwoou 2024-04-27

แปลทั้ง attribute และ property เป็น "แอตทริบิวต์" เหมือนกันไปซะแล้วนะครับ..