Poznajemy HTMLDialogElement, świetną rzecz w HTML5, z której będziemy często korzystać i dziwić się, że kiedyś mogło tego nie być w HTMLu i zachodzić w głowę jak wtedy ludzie sobie radzili. Do dzieła.
Ok, oto przykładowy dialog:
<dialog open>
<button autofocus>Close</button>
<p>This modal dialog has a groovy backdrop!</p>
</dialog>
<button>Show the dialog</button>
Ten dialog ma atrybut open. Jeżeli ten atrybut istnieje, dialog jest pokazywany. Jeżeli go nie ma – nie ma dialogu. Atrybut open odpowiada za pokazywanie dialogu bez backdropu.
Są inne sposoby na pokazywanie dialogu, razem z tłem:
- metoda d.show() otwiera dialog bez backdropu i pozwana na interakcje z jego otoczeniem
- metoda d.close(retVal) zamyka dialog i ewentualny backdrop, może przyjąć return value, które można odczytać poprzez event close
- metoda d.showModal() otwiera dialog razem z backdropem
- formularz w dialogu z method dialog na submit zamyka dialog i przekazuje do close do return value dane z formularza, które można odczytać eventem close
Ok, jak wygląda pseudoelement backdrop, czyli stylujemy tło, które pojawia się w przypadku showModal:
::backdrop {
background-image: linear-gradient(
45deg,
magenta,
rebeccapurple,
dodgerblue,
green
);
opacity: 0.75;
}
Warto zwrócić uwagę, że element też można przekazać i różnie elementy też mogą mieć backdrop:
video::backdrop {
background: hsla(0, 0%, 0%, .5);
}
Ale backdropu z np. alerta już sobie nie ostylujemy, jak nam się nie podoba, to po prostu swój własny alert napiszmy.
Ok, zamykanie jest dość proste, tworzymy w dialogu guzik, robimy event listener, dajemy mu metodę close z dialogu, koniec. Teoretycznie button type submit formmethod dialog też by zamknął podejrzewam.
Ok, ale czasem możemy chcieć mieć formularz. Oto przykład:
<dialog id="my-dialog" >
<form method="dialog">
<p>Would you like to continue?</p>
<button type="submit" value="no">No</button>
<button type="submit" value="yes">Yes</button>
</form>
</dialog>
Te dane lądują w close jako ret val i można je czytać przez dialog.returnValue:
let dialog = document.getElementById("my-dialog");
setTimeout(() => dialog.showModal(), 1000);
dialog.addEventListener("close", function(event) {
alert('Submitted value: "' + dialog.returnValue + '"');
});
Też pewnie dałoby radę czytać z formdata na guzik nie-submit z prevent default poza formem i przekazywać do close, ale to bez sensu by było.
Czytając o tym elemencie, zauważyłem na pewnym blogu ciekawą rzecz. Dopiszmy event listenera:
dialog.addEventListener("click", function(event) {
console.log("click")
});
Gdziekolwiek nie klikniemy odpalany jest event. A teraz ten kod:
dialog.addEventListener("click", e => {
const dialogDimensions = dialog.getBoundingClientRect()
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
console.log("clicked outside")
}
})
Tu mamy taki bajer, że tylko kliknięcie poza dialogiem będzie uznane jako ten event. Można tu coś dopisać:
dialog.addEventListener("click", e => {
const dialogDimensions = dialog.getBoundingClientRect()
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
dialog.close("aborted")
}
})
Teraz mamy submited value aborted, bo to przekazaliśmy do close jako return value a dzieje się to wtedy, gdy mamy odpalony dialog, ale klikniemy poza nim.