Hallo Rolf,
ein paar Gedanken zu dem Beispiel:
<main id="main"> <table id="personen"> <thead> <tr> <th>Name</th><th>Vorname</th><th>Geburtsdatum</th> </tr> </thead> <tbody/> </table> <button id="addPerson">Neue Person</button> </main>document.addEventListener('DOMContentLoaded', function () { let element = document.getElementById ('addPerson'); element.addEventListener ('click', addPersonenRow); }); function addPersonenRow() { let frag = document.createDocumentFragment(); let row = frag.appendChild(document.createElement('tr')); let col1 = row.appendChild(document.createElement('td')); let col2 = row.appendChild(document.createElement('td')); let col3 = row.appendChild(document.createElement('td')); col1.innerHTML = "<input type='text' name='nachname'>"; col2.innerHTML = "<input type='text' name='vorname'>"; col3.innerHTML = "<input type='text' name='gebDat'>"; document.querySelector('#personen').appendChild(frag); // frag.childElementCount ist jetzt 0. }
Statt mit <table id="personen"> den Namen der Tabelle in einem id Attribut zu verstecken, könnte man auch <table aria-label="Personen"> schreiben. Dann ist auch ohne Überschrift ein zugänglicher Name vorhanden. Die Referenz in JavaScript holen wir dann entsprechend über den Attributselektor:
document.querySelector('[aria-label=Personen]')
Davon abgesehen ist die Frage, wie die <input> Elemente an ihre Beschriftung kommen.
Das name Attribut wird gemäß Accessible Name Calculation nicht berücksichtigt. Ich sehe auch nicht, warum das Attribut in diesem Beispiel angegeben werden müsste. Genauso überflüssig ist die Angabe von type="text". Das ist ja der Standardwert.
Die Beschriftung der Eingabefelder sollte sich aus dem textuellen Inhalt des jeweiligen Spaltenkopfs ergeben. Aber sind Browser und Screenreader schlau genug diesen Zusammenhang auch ohne weitere Angaben zuverlässig herzustellen?
Eventuell wäre es sinnvoll, die Verbindung über die scope und/oder headers Attribute auf den <th> bzw. <td> Elementen explizit zu machen:
<tr>
<th scope="col" id="lastname">Name</th>
<th scope="col" id="firstname">Vorname</th>
<th scope="col" id="birthdate">Geburtsdatum</th>
</tr>
col1.setAttribute('headers', 'lastname')
col2.setAttribute('headers', 'firstname')
col3.setAttribute('headers', 'birthdate')
Möglicherweise reicht das schon und eine weitere Angabe direkt auf den <input> Elementen wäre sogar schlecht, da das Label dann doppelt angesagt würde, einmal für die Tabellenzelle und einmal für das Inputelement. Ob das so ist, weiß ich nicht.
Falls eine direkte Angabe sinnvoll ist, dann mittels aria-labelledby, damit die Labels konsistent sind:
col1.innerHTML = '<input aria-labelledby="lastname">'
col2.innerHTML = '<input aria-labelledby="firstname">'
col3.innerHTML = '<input aria-labelledby="birthdate">'
Vielleicht kann jemand der mehr Ahnung davon hat als ich etwas dazu sagen, was hier die beste Vorgehensweise ist?
Was den JavaScript-Code angeht sollten die let durch const ersetzt werden. Es wird ja in keinem Fall ein neuer Wert zugewiesen.
Den Code zum Erstellen der <td> Elemente könnte man auch kürzer schreiben. Die Schnittstelle HTMLTableRowElement stellt die Methode insertCell bereit. Wird kein Index übergeben, fügt sie die Zelle automatisch am Ende der Zeile an. Rückgabewert ist auch hier die Referenz auf das erzeugte <td> Element. Man könnte also schreiben:
const col1 = row.insertCell()
const col2 = row.insertCell()
const col3 = row.insertCell()
Wobei das auch in einer Zuweisung an innerHTML zusammengefasst werden könnte:
// addPersonenRow ?
function addPersonRow() {
const fragment = new DocumentFragment
const row = fragment.appendChild(document.createElement('tr'))
row.innerHTML = `
<td headers="lastname">
<input aria-labelledby="lastname">
</td>
<td headers="firstname">
<input aria-labelledby="firstname">
</td>
<td headers="birthdate">
<input aria-labelledby="birthdate">
</td>
`
const table = document.querySelector('[aria-label=Personen]')
table.appendChild(fragment)
}
Möglicherweise könnte man auch die entsprechenden Werte aus der zugeordneten Tabelle auslesen und dann mittels Templatesyntax in das Markup einfügen, um sich keine unnötige Abhängigkeit zu schaffen. Vielleicht soll ja mal eine Spalte "Lieblingsfarbe" hinzugefügt werden. Aber das wäre für das worum's hier geht wahrscheinlich unnötige Komplexität.
Wollte an dem Beispiel im Wiki jetzt nicht rumpfuschen, da ich wie gesagt selbst nicht sicher bin, wie hier das beste Vorgehen wäre, aber vielleicht kann man ja den ein oder anderen Vorschlag einbauen um das Beispiel zu verbessern.
Edit: Wobei das Beispiel halt insgesamt nicht so richtig sinnvoll ist, da wir ohnehin einen gemeinsamen Elternknoten für den hinzuzufügenden Abschnitt haben. Da lohnt sich der Umweg über DocumentFragment also nicht wirklich.
Vielleicht eher sowas wie:
const list = document.getElementById('mylist')
const items = [
'Item 1',
'Item 2',
'Item 3'
]
const fragment = new DocumentFragment
items.forEach(function(item) {
const li = document.createElement('li')
li.textContent = item
fragment.appendChild(li)
})
list.appendChild(fragment)
Viele Grüße,
Matthias