createDocumentFragment Beispiel

- escaping
- essays
- imei
Hallo !
Kann es sein, dass im Beispiel fuer createDocumentFragment:
let element = document.getElementById ('addPerson');
element.addEventListener ('click', addPersonenRow);
}
" )" fehlt ?
Effel
Moin effel,
Kann es sein, dass im Beispiel fuer createDocumentFragment:
document.addEventListener('DOMContentLoaded', function () {
let element = document.getElementById ('addPerson');
element.addEventListener ('click', addPersonenRow);
}
" )" fehlt ?
Effel
https://wiki.selfhtml.org/wiki/JavaScript/DOM/Document/createDocumentFragment#Anwendungsbeispiel ist korrigiert. Danke!
Gruß,
Hallo Ryuno-Ki,
Danke schön!
Rolf
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
Hallo Matthias,
ich weiß nicht, ob das Beispiel überhaupt was taugt. Ein DocumentFragment von Hand zu schnitzen käme mir vermutlich nicht in den Sinn, ich würde, ähnlich wie Du, ein Template-Literal verwenden, aber eins, das das <tr>-Element schon enthält. Und diesen Wert mit insertAdjacentHTML("beforeend", html) an den Table-Body anhängen.
DocumentFragment-Objekte finde ich eher im Zusammenhang mit HTML <template> sinnvoll, da erstellt man sie nicht selbst, sondern bekommt sie vom Browser.
Ob eine Tabelle aus Eingabefeldern bedienbares und zugängliches HTML ist, steht dann wieder auf einem ganz anderen Blatt.
Rolf
Hi,
<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>
// 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) }
egal, wie das Fragment (ne Tabellenzeile) zusammengeschraubt wird, die gehört nicht als Kind an die table, sondern als Enkel - mit dem tbody als Elternteil.
cu,
Andreas a/k/a MudGuard