@@MH
Ich hab mal ein Bild von dem Formular gemacht:
OK, eine Gruppe mit zwei Eingabefeldern und einem Zeile-löschen-Button (ich würde das eher als „diese Person löschen“ bezeichnen). Eingabefelder werden mit fieldset
gruppiert:
<fieldset>
<legend>Person hinzufügen</legend>
<div>
<label>
Vorname
<input name="givenName[]"/>
</label>
<label>
Nachname
<input name="familyName[]"/>
</label>
<button class="control-delete-person" type="button" hidden="">diese Person löschen</button>
</div>
</fieldset>
Davon kann es später mehrere geben (deshalb class
, nicht id
), wenn welche mit JavaScript hinzugefügt werden. Dazu gibt es einen Zeile-hinzufügen-Button (den ich lieber „weitere Person hinzufügen“ beschriften würde) und einen Submit-Button fürs Formular. Da die Buttons ohne JavaScript nutzlos sind, sind sie erstmal per hidden
-Attribut versteckt, bis das JavaScript ausgeführt wird.
<form>
<fieldset>
<legend>Person hinzufügen</legend>
<div>
<label>
Vorname
<input name="givenName[]"/>
</label>
<label>
Nachname
<input name="familyName[]"/>
</label>
<button class="control-delete-person" type="button" hidden="">diese Person löschen</button>
</div>
</fieldset>
<p>
<button id="control-add-person" type="button" hidden="">weitere Person hinzufügen</button>
</p>
<p>
<button type="submit">abschicken</button>
</p>
</form>
Das JavaScript entfernt dann als erstes das hidden
und macht die Buttons sichtbar:
const formElement = document.querySelector('form');
const firstFieldsetElement = formElement.querySelector('fieldset');
const firstPElement = formElement.querySelector('p');
formElement.querySelectorAll('button[hidden]').forEach(element => {
element.hidden = false;
});
Die Buttons bekommen ihre Funktion – per event delegation: es wird auf click
-Evemts fürs ganze Formular gelauscht und im Eventhandler wird geprüft, ob das click
durch einen Button ausgelöst wurde und wenn ja, durch welchen. Dadurch muss man bei den später hinzukommenden Buttons nicht noch Eventhandler registrieren.
formElement.addEventListener('click', event => {
if (event.target.id == 'control-add-person')
{
// füge Eingabefelder für weitere Person hinzu
}
else if (event.target.classList.contains('control-delete-person'))
{
// enferne Eingabefelder für diese Person
}
});
Um Eingabefelder für weitere Person hinzuzufügen, wird das fieldset
geklont. Die dabei mit geklonten Werte der Eingabefelder müssen gelöscht werden. Außerdem wird der Fokus auf die neu eingefügte Gruppe gelegt, welches deshalb tabIndex
−1 hat. (Vielleicht wäre es sogar besser, gleich das erste Eingabefeld in der Gruppe zu fokussieren – mal jemanden fragen, der sich damit auskennt.)
if (event.target.id == 'control-add-person')
{
const clone = document.importNode(firstFieldsetElement, true);
clone.querySelectorAll('input').forEach(element => {
element.value = null;
});
formElement.insertBefore(clone, firstPElement);
clone.tabIndex = -1;
clone.focus();
}
Beim Entfernen wird das zu dem auslösenden Button gehörige fieldset
-Element ermittelt und aus dem DOM entfernt.
else if (event.target.classList.contains('control-delete-person'))
{
formElement.removeChild(event.target.closest('fieldset'));
}
Per CSS werden die für die für die visuelle Darstellung nicht benötigten Beschriftungen visuell versteckt – aber so, dass sie von Screenreadern immer noch vorgelesen werden, also nicht display: none
oder visibility: hidden
. Das betrifft die legend
-Elemente, die Beschriftungen der Eingabefelder außer denen in der ersten Gruppe und die Person-löschen-Buttons, die durch ❌ visuell repräsentiert werden.
legend,
fieldset:not(:first-child) label,
.control-delete-person
{
font-size: 0;
}
Das Ganze ist in diesem Codepen zu sehen.
Man könnte den Fokus von fieldset
auch durch eine Hintergrundfarbe visualisieren und dann die outline
entfernen (im Pen auskommentiert).
fieldset:focus
{
background: #eef;
outline: none;
}
fieldset:focus-within
{
background: #eef;
}
Und wenn man will, die :focus
-Effekte auch für :hover
angeben …
LLAP 🖖
“When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory