JSKid: modale Fenster

Hallo zusammen

Modale Fenster mit JS: dazu gibt es zuhauf Einträge im Netz. Ich habe früher Visual Basic 6 programmiert und auch dort gibt es modale Fenster und zwar in dem Sinne

Die Form, die den modalen Dialog aufruft, übernimmt nach dem Klicken die Eintragungen des Benutzers im Dialog, wenn der Benutzer OK geklickt hat, oder verwirft sie beim Klicken auf die Abbrechen-Schaltfläche

Bei allem was ich im Internet gefunden habe, ist mir nicht klar geworden, ob Javascript den Programmfluss anhalten kann, wenn ein modales Fenster aufgerufen wurde und die dort gemachten Eintragungen dann später im Hauptfenster verarbeiten kann.

Also wenn mir jemand das einfach beantworten kann und am besten noch mit einem Beispiel verdeutlichen, dann wäre ich sehr dankbar dafür.

Viele Grüße

  1. Hallo JSKid,

    Bei allem was ich im Internet gefunden habe, ist mir nicht klar geworden, ob Javascript den Programmfluss anhalten kann, wenn ein modales Fenster aufgerufen wurde und die dort gemachten Eintragungen dann später im Hauptfenster verarbeiten kann.

    Es gibt window.confirm, window.alert und window.prompt, die genau das tun: den Programmfluss anhalten, bis der User seine Eingabe getätigt hat.

    Das vorausgesagt wirst du auf Probleme stoßen, wenn du das benutzt. Z.B. werden diese Dialogfenster im Chrome automatisch geschlossen (dismissed), wenn der Tab gewechselt wird. Wenn der Tab nicht aktiv ist, werden sie gar nicht erst angezeigt. Google und Mozilla raten explizit von der Verwendung ab.

    LG,
    CK

    -- https://wwwtech.de/about

    Folgende Nachrichten verweisen auf diesen Beitrag:

  2. Tach!

    Bei allem was ich im Internet gefunden habe, ist mir nicht klar geworden, ob Javascript den Programmfluss anhalten kann, wenn ein modales Fenster aufgerufen wurde und die dort gemachten Eintragungen dann später im Hauptfenster verarbeiten kann.

    Nein, kann es nicht. Javascript ist single-threaded und du musst den Thread wieder freigeben, damit der Nutzer den Dialog selbst bedienen kann. Es gibt dann irgendwann ein Event, wenn das Formular abgesendet wurde oder der OK/Cancel-Button aktiviert wurde oder Backdrop geklickt oder Esc gedrückt oder was auch immer. In diesem Event kann man das Ergebnis des Dialogs aufbereiten und ruft dann eine Aktion auf, die damit was tun soll. Somit ist aber das Aufrufen des Dialogs und das Abarbeiten des Ergebnisses an getrennten Stellen im Code. Besser ist, wenn der Dialog-Aufruf ein Callback übergibt (oder zwei: eins für OK, eins für Cancel) und oben genanntes Event dieses Callback aufruft. Und dann kommt man an die Stelle, an der der Einsatz eines Promise sinnvoll ist. Die sind für asynchrones Arbeiten geschaffen, und so ein Dialog arbeitet ja auch nach diesem Prinzip. Der OK-Button (und verwandte Bedienhandlungen) löst das Promise (resolve), Cancel und Co. weisen es zurück (reject).

    dedlfix.

    Folgende Nachrichten verweisen auf diesen Beitrag:

    1. Ok, Vielen Dank für die Erlärung. Wie gesagt im Netz gibt es etliche Erklärungen und Beschreibungen wie modale Fenster erzeugt und benutzt werden. Das würde doch bedeuten, dass ein modales Fenster dann erst am Ende des Programm-Flußes angezeigt wird und lediglich eine Info beinhaltet.

      1. Hallo

        Ok, Vielen Dank für die Erlärung. Wie gesagt im Netz gibt es etliche Erklärungen und Beschreibungen wie modale Fenster erzeugt und benutzt werden.

        Könntest du bitte einmal erklären, was du unter einem „modalen Fenster“ verstehst‽

        In den Antworten ist nämlich von zwei verschiedenen Techniken die Rede.

        1. Die modalen Fenster, die JavaScript selbst bereitstellt. Das wäre window.confirm, window.alert und window.prompt. Die warten, wie Christian bereits beschrieb, aber ihre ebenfalls beschriebenen Tücken haben.
        2. Modale Dialoge, die mit HTML-Elementen in einem vorhandenen HTML-Dokument erzeugt werden, wie sie in diesem Forum zum Beispiel für das erzeugen eines Links in einem Posting benutzt werden. In diesem Fall trifft dedlfix' Beschreibung zu, die da sagt, bei einem Event diese Elemente zu erzeugen und einzublenden sowie zum Ende der Programmausführung auch neue Events festzulegen, die bei Aktionen innerhalb des modalen Dialogs ausgelöst werden. Wenn eines dieser neuen Events anschlägt, wird ein neues Programm/eine neue Funktion ausgeführt, um beispielsweise eine Formulareingabe zu prüfen, an den Server zu senden und den Dialog zu schließen.

        Das würde doch bedeuten, dass ein modales Fenster dann erst am Ende des Programm-Flußes angezeigt wird und lediglich eine Info beinhaltet.

        Oder eben interaktive Elemente, mit denen später etwas geschehen soll/kann.

        Tschö, Auge

        -- Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
        Hohle Köpfe von Terry Pratchett
        1. Ich denke ich spreche von der Technik unter 1.) Nur sind die drei genannten "Fenster" nicht sehr schön und ich hätte sie gerne mit einem eigenen Stil versehen und halt den Progamm-Fluß genauso unterbrechen, bis der User etwas eingibt.

          Aber so wie ich dedlfix verstanden habe ist das nicht möglich.

          1. Tach!

            Ich denke ich spreche von der Technik unter 1.) Nur sind die drei genannten "Fenster" nicht sehr schön und ich hätte sie gerne mit einem eigenen Stil versehen und halt den Progamm-Fluß genauso unterbrechen, bis der User etwas eingibt.

            Aber so wie ich dedlfix verstanden habe ist das nicht möglich.

            Richtig. confirm() und alert() halten das was im DOM passiert an. Der angezeigte Dialog findet unabhängig davon statt. Erst nach Bestätigung wird die DOM-Arbeit fortgesetzt.

            Ein selbst ersteller Dialog mit DOM-Elementen hingegen ist auf ein funktionierendes DOM angewiesen und kann dessen Tätigkeiten nicht anhalten, ohne selbst betroffen zu sein. Deswegen können Dialoge abseits von confirm() und alert() nicht synchron gestaltet werden.

            dedlfix.

          2. Hallo

            Ich denke ich spreche von der Technik unter 1.)

            Gut, also die JS-eigenen Dialoge mit der Gefahr, dass ihre Anzeige von den Browsern von vornherein unterdrückt wird.

            Nur sind die drei genannten "Fenster" nicht sehr schön und ich hätte sie gerne mit einem eigenen Stil versehen …

            Womit wir bei Technik Nr. 2, HTML-Elemente als Dialog zu erzeugen, wären.

            … und halt den Progamm-Fluß genauso unterbrechen, bis der User etwas eingibt.

            Ich weiß nicht, warum du dich so sehr auf die Unterbrechung des Programmflusses kaprizierst. Dass das bei Technik Nr. 2 nicht geht, ist klar. Dass diese Unterbrechnung unnötig ist und durch einen geteilten Programmablauf ersetzt wird, hat dedlfix lang und breit erklärt.

            Beispiel:

            <!DOCTYPE html> <html lang="de"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- mehr Elemente --> <title>Dialogtest</title> </head> <body> <header> <h1>Dialogtest</h1> <nav id="mainmenu"> <ul> <!-- diverse Listenelemente --> <li><a href="?dataset=search" class="indoc">Adressen suchen</a></li> </ul> </nav> </header> <main> <!-- diverse Elemente --> </main> <template id="template-search"> <dialog id="popupform" class="entryform simple" open> <header class="titlebar"> <h2>Suche von Adressen</h2> </header> <p>Teileingaben sind mit angehängtem Sternchen (*) möglich. Ohne Stern wird <em>exakt</em> nach dem eingegebenen Wert gesucht.</p> <form method="post" id="entryform"> <fieldset> <legend>Suchparameter</legend> <div> <label for="id-strasse">Straße, Hausnummer</label> <input type="text" id="id-strasse" name="fld-strasse"> </div> <div> <label for="id-plz">PLZ</label> <input type="text" id="id-plz" name="fld-plz"> </div> <div> <label for="id-ort">Ort</label> <input type="text" id="id-ort" name="fld-ort"> </div> </fieldset> <p> <button name="btn-search" id="id-btn-search">Suche ausführen</button> <button type="reset">Eingaben zurücksetzen</button> <button type="button" id="close-popupform">Formular schließen</button> </p> </form> </dialog> <div class="backdrop"></div> </template> </body> </html>

            In einem HTML-Dokument wird per Template ein Suchformular bereitgehalten. Per JS wird ein Eventhandler für den Link im Hauptmenü der Seite registriert …

            document.addEventListener('DOMContentLoaded', function() { document.getElementById('mainmenu').addEventListener('click', runActionMainMenu, true); document.getElementById('mainmenu').addEventListener('touchstart', runActionMainMenu, true); });

            … damit auf den Klick des Links reagiert werden kann, um den Dialog einzublenden.

            function runActionMainMenu(event) { var c, i, em = null; var elem = event.target; if (elem.classList.contains('indoc')) { var url = elem.getAttribute('href'); if (url == null) { return null; } var urlTarget = gup('dataset', url); // gup: Hilfsfunktion, um an den Wert des URL-Parameters zu kommen if (urlTarget == 'search') { var em = document.querySelector('#template-search').content; // finde das richtige Template } if (em !== null) { var clone = document.importNode(em, true); // importiere den Inhalt des Templates in die Variable clone document.querySelector('main').appendChild(clone); // hänge den Inhalt des Templates ins DOM ein /** * Der Dialog wurde bereitgestellt, das Programm kann beendet werden, wenn … * … die Events zum absenden und schließen des Dialogs registriert wurden */ document.getElementById('entryform').addEventListener('submit', searchDatasets, true); document.getElementById('close-popupform').addEventListener('click', closePopupForm, true); } else { console.log("kein zur Aktion (" + urlTarget + ") passendes Template gefunden."); } event.preventDefault(); // verhindere, dass dem angegebenen Linkziel gefolgt wird } }

            In closePopupForm werden die Events zum schließen des Dialogs deregistriert und der Dialog aus dem DOM entfernt und in searchDatasets wird zudem zuvor per Ajax/Fetch die Suchanfrage an den Server gestellt. Und auch dort geht man nach dem selben Prinzip vor, eine Aufgabe zu erledigen, und zu definieren, was bei der irgendwannn auftretenden reaktion zu erfolgen hat (asynchrone Abarbeitung).

            function closePopupForm(event) { var elem = event.target; var dialog = document.getElementById('popupform'); var backdrop = document.getElementsByClassName('backdrop'); if (elem.id == 'close-popupform' || elem.id == 'close-popupform-icon') { document.getElementById('close-popupform').removeEventListener('click', closePopupForm, true); dialog.remove(); for (var i = 0; i < backdrop.length; i++) { backdrop[i].remove(); } } return false; }

            Zu guter Letzt gehört der Dialog zum Rest der Seite passend per CSS formatiert.

            dialog { position: fixed; left: 0; right: 0; top: 2em; margin: auto; padding: 1rem; border: 1px solid #cbd4d8; border-radius: 0.25rem; background-color: #fff; display: block; z-index: 3; } dialog + .backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: rgba(0, 0, 0, 0.375); z-index: 2; }

            Du brauchst also keine Möglichkeit, dass das Programm auf etwas wartet und bis dahin keine anderen Aufgaben erfüllt, sondern nur eine Trennung der Aufgaben in Erzeugung des Dialogs und die Reaktionen auf die möglichen Eingaben.

            Das Ganze geht bestimmt auch eleganter und eine Bedienung des Formulars per Touch habe ich im Codebeispiel nicht berücksichtigt, aber zur Verdeutlichung des Prinzips sollte es, so hoffe ich, reichen.

            Tschö, Auge

            -- Ein echtes Alchimistenlabor musste voll mit Glasgefäßen sein, die so aussahen, als wären sie beim öffentlichen Schluckaufwettbewerb der Glasbläsergilde entstanden.
            Hohle Köpfe von Terry Pratchett
          3. Liebe(r) JSKid,

            Aber so wie ich dedlfix verstanden habe ist das nicht möglich.

            richtig, wenn Du die window-Methoden alert, confirm und prompt verschönern willst, musst Du sie komplett mit anderen Mitteln nachbauen und dabei eine asynchrone Vorgehensweise einsetzen.

            Liebe Grüße,

            Felix Riesterer.

      2. Tach!

        Ok, Vielen Dank für die Erlärung. Wie gesagt im Netz gibt es etliche Erklärungen und Beschreibungen wie modale Fenster erzeugt und benutzt werden. Das würde doch bedeuten, dass ein modales Fenster dann erst am Ende des Programm-Flußes angezeigt wird und lediglich eine Info beinhaltet.

        Nein. Das modale Fenster ist erstmal nur eine Änderung des DOM, dergestalt dass die bisherigen Elemente der Seite überlagert und oft auch unbedienbar gemacht werden. Ansonsten verhalten sich die Dialog-Elemente nicht anders als andere HTML-Elemente, was die Bedienbarkeit und Interaktivität angeht. Nachdem diese Dialog-Elemente ins DOM eingebracht wurden, beendet man zunächst seine Arbeit (sprich: den Eventhandler, der zur Erstellung der Dialog-Elemente geführt hat) und der Programmfluss geht wieder an den Browser zurück, der sich wieder der Abarbeitung der Event Loop widmet oder idlet, wenn darin nichts zu finden ist. Es ist nun möglich, dass der Anwender Bedienhandlungen vornehmen kann, die der Browser wie üblich behandelt. Wenn es beispielsweise ein Klick auf den OK-Button ist, dann löst er dafür ein Event aus. Du kannst einen entsprechenden Eventhandler schreiben, der daraufhin die Elemente des Dialogs aus dem DOM entfernt, so dass die ursprünglichen Elemente wieder sichtbar und bedienbar werden. Der Eventhandler kann hier nun einfach enden, wenn nichts weiter zu tun ist. Oder aber, er ruft eine vorher definierte Funktion auf, die irgendwas macht, was auch immer getan werden soll. Das Aufrufen einer vordefinierten Funktion ist aber recht unflexibel, weswegen man generell lieber einen beim Erstellen übergebenen Callback aufruft, oder zu Mechanismen wie dem Promise greift.

        dedlfix.

  3. Liebe(r) JSKid,

    ich habe zum Thema modale Fenster und JavaScript einen Artikel erstellt: Eigene modale Dialogfenster. Dieser Artikel bietet zunächst einen Ersatz für alert, confirm und prompt an, wird aber in einer späteren Erweiterung komplexere eigene Dialoge präsentieren. Die dazu notwendigen Grundlagen und Voraussetzungen werden aber schon in der jetzigen Fassung erklärt und dargestellt.

    Liebe Grüße,

    Felix Riesterer.

    1. Servus!

      ich habe zum Thema modale Fenster und JavaScript einen Artikel erstellt: Eigene modale Dialogfenster.

      Den kannte ich noch gar nicht. Ach so: Niegel-nagelneu!

      Vielen Dank!

      Herzliche Grüße

      Matthias Scharwies

      -- "I don’t make typos. I make new words."
    2. Hallo Felix,

      ich finde es gut, das du dich den Thema gewidmet hast. Ich habe den Artikel jetzt nur überflogen, aber dabei sind mir zwei Dinge aufgefallen:

      • Du verwendest Einbuchstaben-Variablen, z.B. "s" für den Anzeigestring. Ich mache das auch gerne, aber in einem Tutorial sollten die Variablennamen sprechend sein.

      • Du hast das Thema "Den Focus fangen" ausgeklammert. (Das habe ich bei meinen Warnfenstern auch gemacht.) Die Frage ist: sollten das in diesen Artikel mit rein, oder ist das so unwichtig, dass man da erst mal drauf verzichten kann?

      Gruß
      Jürgen

      1. Hallo JürgenB,

        • Du hast das Thema "Den Focus fangen" ausgeklammert. (Das habe ich bei meinen Warnfenstern auch gemacht.) Die Frage ist: sollten das in diesen Artikel mit rein, oder ist das so unwichtig, dass man da erst mal drauf verzichten kann?

        Focus-Management für Modals ist ein essentielles A11y-Feature. Auf die Behandlung sollte man auf keinen Fall verzichten.

        LG,
        CK

        -- https://wwwtech.de/about
        1. Hallo Christian,

          dann sollte man Felix zumindestens mit Ideen versorgen.

          Meine spontane ungetestete Idee: auf das Blur-Event von der Modal-Box lauschen und dann den Focus zurückholen.

          Gruß
          Jürgen

          1. Hallo JürgenB,

            dann sollte man Felix zumindestens mit Ideen versorgen.

            Ja. Leider bin ich gerade mal wieder unterwegs, deshalb hatte/habe ich keine Zeit für eine ausführlichere Antwort… im Wesentlichen läuft es darauf hinaus, dass man bei aktivem Modal überwachen muss, dass der Fokus nicht auf ein Element ausserhalb des Modals fällt. Es gibt da ein paar Beispiel-Projekte, etwa das accessible modal dialog oder das a11y-dialog, ein Fork und eine Weiterentwicklung von dem Accessible Modal Dialog.

            Meine spontane ungetestete Idee: auf das Blur-Event von der Modal-Box lauschen und dann den Focus zurückholen.

            Weiss nicht recht, ob das reicht. Die beiden verlinkten Projekte machen schon etwas mehr. Ein Blick in deren Code lohnt sich.

            LG,
            CK

            -- https://wwwtech.de/about
            1. Lieber Christian,

              im Wesentlichen läuft es darauf hinaus, dass man bei aktivem Modal überwachen muss, dass der Fokus nicht auf ein Element ausserhalb des Modals fällt. Es gibt da ein paar Beispiel-Projekte, etwa das accessible modal dialog oder das a11y-dialog, ein Fork und eine Weiterentwicklung von dem Accessible Modal Dialog.

              das sind doch prima Links um unter "Weblinks" oder "Siehe auch" hinzugefügt zu werden! Im Rahmen des Tutorials kann ich die Problematik nur aufzeigen, keinesfalls aber zur Zufriedenheit aller möglicher Profis vollumfänglich lösen. Daher mein Dank für diese Anstöße, ich werde sehen, was ich damit tue.

              Liebe Grüße,

              Felix Riesterer.

              1. Hallo Ingrid,

                das sind doch prima Links um unter "Weblinks" oder "Siehe auch" hinzugefügt zu werden!

                na wie gut, dass sie dort jetzt stehen.

                Liebe Grüße,

                Felix Riesterer.

          2. Lieber JürgenB,

            dann sollte man Felix zumindestens mit Ideen versorgen.

            dieser Dein Satz wärmt mein Herz! Sagt er doch in etwa: Wenn ihr kritisiert, dann bitte konstruktiv!

            Liebe Grüße,

            Felix Riesterer.

            1. Hallo Felix,

              dann sollte man Felix zumindestens mit Ideen versorgen.

              dieser Dein Satz wärmt mein Herz! Sagt er doch in etwa: Wenn ihr kritisiert, dann bitte konstruktiv!

              Du hast recht. Tut mir leid. Ich war, als ich das geschrieben habe, im Zug unterwegs und musste mein Zeug zusammenpacken, deshalb war das so kurz.

              LG,
              CK

              -- https://wwwtech.de/about
      2. Lieber JürgenB,

        • Du verwendest Einbuchstaben-Variablen, z.B. "s" für den Anzeigestring. Ich mache das auch gerne, aber in einem Tutorial sollten die Variablennamen sprechend sein.

        wenn der Erklärtext direkt nach dem Code nicht genügt, werde ich s zu str erweitern.

        • Du hast das Thema "Den Focus fangen" ausgeklammert. (Das habe ich bei meinen Warnfenstern auch gemacht.) Die Frage ist: sollten das in diesen Artikel mit rein, oder ist das so unwichtig, dass man da erst mal drauf verzichten kann?

        Ich habe keine Ahnung wohin ich den Fokus nach dem Schließen des Dialogs setzen soll. Woher soll ich denn wissen, wo er davor war?

        Etwas anderes ist es, den Fokusverlust bei einem offenen Dialogfenster zu unterbinden bzw. reparieren. Wohin soll er denn als Default zurück geführt werden? Auf den Schließen-Button wie schon zu Anfang beim Anzeigen des Dialogs?

        Liebe Grüße,

        Felix Riesterer.

        1. Hallo Felix,

          ... werde ich s zu str erweitern."

          ich dachte da eher an „Anzeigetext“, „Callback_bei_OK“, …

          Ich habe keine Ahnung wohin ich den Fokus nach dem Schließen des Dialogs setzen soll. Woher soll ich denn wissen, wo er davor war?

          Idee: über das Focus-Event merken, wer den Focus hatte, bis das modale Fenster geöffnet wurde.

          Etwas anderes ist es, den Fokusverlust bei einem offenen Dialogfenster zu unterbinden bzw. reparieren. Wohin soll er denn als Default zurück geführt werden? Auf den Schließen-Button wie schon zu Anfang beim Anzeigen des Dialogs?

          bei Shift-Tab zum letzten fokussierbaren Element springen, sonst zum ersten; oder auch immer zum beim Öffnen fokussierten.

          Gruß
          Jürgen

          1. Tach!

            Ich habe keine Ahnung wohin ich den Fokus nach dem Schließen des Dialogs setzen soll. Woher soll ich denn wissen, wo er davor war?

            Idee: über das Focus-Event merken, wer den Focus hatte, bis das modale Fenster geöffnet wurde.

            Im Zweifelsfall ist das der Button, dessen Betätigung zum Öffnen des Dialogs geführt hat. Ob es sinnvoll ist, den Focus wieder dorthin zu setzen, ist vielleicht auch eine Frage des Anwendungsfalles.

            dedlfix.

            1. Hallo dedlfix,

              Ich habe keine Ahnung wohin ich den Fokus nach dem Schließen des Dialogs setzen soll. Woher soll ich denn wissen, wo er davor war?

              Idee: über das Focus-Event merken, wer den Focus hatte, bis das modale Fenster geöffnet wurde.

              Im Zweifelsfall ist das der Button, dessen Betätigung zum Öffnen des Dialogs geführt hat. Ob es sinnvoll ist, den Focus wieder dorthin zu setzen, ist vielleicht auch eine Frage des Anwendungsfalles.

              wenn das Öffnen auf eine Useraktion erfolgte, ja. Wenn das Fenster aber z.B. eine automatisch geöffnete Fehlermeldung ist, dann wird es schwieriger.

              Gruß
              Jürgen

    3. Tach!

      ich habe zum Thema modale Fenster und JavaScript einen Artikel erstellt: Eigene modale Dialogfenster. Dieser Artikel bietet zunächst einen Ersatz für alert, confirm und prompt an, wird aber in einer späteren Erweiterung komplexere eigene Dialoge präsentieren. Die dazu notwendigen Grundlagen und Voraussetzungen werden aber schon in der jetzigen Fassung erklärt und dargestellt.

      Sehr schön bis hier her, und nun zur Kritik. Ein alert() hat keine Unterscheidung zwischen OK und Abbrechen, es gibt nur die Lesebestätigung. Man sollte da also nur einen Handler angeben müssen/können.

      Als Fortsetzung bietet sich an, über Promises nachzudenken.

      dedlfix.

      1. Lieber dedlfix,

        Als Fortsetzung bietet sich an, über Promises nachzudenken.

        um das - für mich bisher nur dem Namen nach bekannte - Thema vorerst nicht anschneiden zu müssen, habe ich diesen ergänzenden Callback verwendet. Aber ja, das Thema liegt hier definitiv auf der Hand. Sobald ich mehr davon verstehe und wieder mehr Zeit habe, werde ich mir eine passende Ergänzung überlegen. Außerdem müsste man dann den Artikel auf mehrere Seiten auftrennen. Die Seite ist jetzt schon sehr lang...

        Liebe Grüße,

        Felix Riesterer.

        1. Tach!

          Als Fortsetzung bietet sich an, über Promises nachzudenken.

          Aber ja, das Thema liegt hier definitiv auf der Hand. Sobald ich mehr davon verstehe und wieder mehr Zeit habe, werde ich mir eine passende Ergänzung überlegen.

          Als Tipp für den Einstieg: Meiner Erfahrung nach sollte man sich dem Thema Promises von zwei Seiten her nähern. Zum einen als Verwender und das ist der einfache Teil. Die meisten werden nur mit so etwas in Berührung kommen:

          dialog.open(parameters) .then(result => ...) .catch(error => ...);

          Wenn man damit klarkommt (man kann das zum Beispiel mit der Fetch-API üben), kommt die andere Seite dran, ein Promise zu erstellen.

          function start () { return new Promise((resolve, reject) => { if (window.confirm('Ja/Nein')) { resolve(42); } else { reject(0); } }); } start() .then(result => { console.log('Die Antwort ist', result); }) .catch(error => { console.log('Abgebrochen'); });

          Sieht auch noch easy aus, aber wir müssen ja die Steuerung unverrichteter Dinge zurückgeben und bekommen das Zepter nur aufgrund von Events oder ähnlichem wieder, um das Promise erfüllen oder zurückweisen zu können. Das müssen wir dann etwas schachteln, damit wir eine Closure bekommen, die und das resolve und reject festhält. Das sieht dem Prinzip nach so aus:

          function start () { return new Promise((resolve, reject) => { ok_button.addEventListener('click', () => { resolve(42); }); cancel_button.addEventListener('click', () => { reject(0); }); dialog.open(); }); } start() .then(result => { console.log('Die Antwort ist', result); }) .catch(error => { console.log('Abgebrochen'); });

          dedlfix.

          1. Lieber dedlfix,

            function start () { return new Promise((resolve, reject) => { ok_button.addEventListener('click', () => { resolve(42); }); cancel_button.addEventListener('click', () => { reject(0); }); dialog.open(); }); } start() .then(result => { console.log('Die Antwort ist', result); }) .catch(error => { console.log('Abgebrochen'); });

            AHA! Also muss die aufgerufene Fuktion ein Promise-Objekt zurückliefern. Diesem Promise-Objekt kann man mit der then-Methode eine (Callback-)Funktion für den resolve-Fall geben, mit der catch-Methode eine für den reject-Fall.

            Ist das so richtig? Wenn ja, dann sieht das schon ziemlich aus wie meine callBack-Sammlung, nur dass diese im Zweifel noch zwischen "cancel" und "abort" unterscheiden kann, also neben resolve und reject noch abort liefert. Ein Promise-Objekt kann das also nicht? Das fände ich dann für mein Tutorial unzureichend!

            Ich lese gerade nach und sehe, dass man mehrere then-Methodenaufrufe machen kann, und dass Promise-Objekte wesentlich komplexer sind (Status-Werte, innere und äußere Promises, Exceptions) als meine Callback-Lösung, die ich doch als recht elegant und für den vorliegenden Fall als bequem empfinde. Wenn man jetzt noch die Buttons frei benennen könnte, wäre sie sogar luxuriös, aber das will ich in diesem Tutorial nicht auch noch behandeln.

            Liebe Grüße,

            Felix Riesterer.

            1. Tach!

              AHA! Also muss die aufgerufene Fuktion ein Promise-Objekt zurückliefern. Diesem Promise-Objekt kann man mit der then-Methode eine (Callback-)Funktion für den resolve-Fall geben, mit der catch-Methode eine für den reject-Fall.

              Ist das so richtig?

              Ja.

              Wenn ja, dann sieht das schon ziemlich aus wie meine callBack-Sammlung, nur dass diese im Zweifel noch zwischen "cancel" und "abort" unterscheiden kann, also neben resolve und reject noch abort liefert. Ein Promise-Objekt kann das also nicht? Das fände ich dann für mein Tutorial unzureichend!

              Was ist der Unterschied zwischen Cancel und Abort? Für mich ist ein Dialog erfolgreich, wenn Daten zurückzugeben sind und abgewiesen (rejected), wenn der Nutzer ihn abgebrochen hat. Meist muss man auf das Abbrechen nicht reagieren, wenn doch, kann man den Fall catch()en. Wenn es wichtig ist, unterschiedliche Abbruch-Situationen unterscheiden zu können, kann man das zwar einerseits über den error-Wert tun. Aber sind das dann andererseits immer noch Abbrüche? Ich denke, dass das dann eher unterschiedliche Rückgabewerte für den Erfolgsfall sind (resolve). Der Rückgabewert kann durchaus komplexer natur sein, also zum Beispiel auch ein Objekt mit Status-Eigenschaft und Content-Eigenschaft. Also wenn der Dialog ein Eingabefeld hat und mehrere Buttons, dann käme die Information zum ausgelösten Button in die Status-Eigenschaft und der Eingabewert ist der Content.

              Ich lese gerade nach und sehe, dass man mehrere then-Methodenaufrufe machen kann, und dass Promise-Objekte wesentlich komplexer sind (Status-Werte, innere und äußere Promises, Exceptions) als meine Callback-Lösung, die ich doch als recht elegant und für den vorliegenden Fall als bequem empfinde. Wenn man jetzt noch die Buttons frei benennen könnte, wäre sie sogar luxuriös, aber das will ich in diesem Tutorial nicht auch noch behandeln.

              Wir wollen das nicht zu einem Tutorial über Promises machen. Es soll lediglich gezeigt werden, wie durch die Verwendung von Promises der Verwender seinen Code besser strukturieren kann. Statt herumliegender Callback-Funktionen, die man ansonsten für nichts anderes braucht, hängen diese direkt über then/catch am Dialog-Öffner. Zugegeben, man könnte die Callbacks auch direkt als Funktion im Aufruf von myAlert() etc. notieren, aber sowas macht die Geschichte nicht lesbarer.

              Schau dir mal an, wie ein Ajax-Request herkömmlich verglichen mit fetch() aussieht.

              var req = new XMLHttpRequest(); req.addEventListener("load", machwas_mit_daten); req.open(url); req.send(); //---- fetch(url) .then(machwas_mit_daten);

              Der (hier gekürzte) fetch()-Aufruf liest sich auch gleich flüssiger. Statt einzelner Anweisungen ist das schon fast ein Satz. Vor allem kommt die Reihenfolge besser zur Geltung: erst den Request abarbeiten, dann mit den Daten arbeiten.

              Mehrere then() brauchst du für den Dialog eher nicht. Das ist kein switch-Ersatz, sondern kann man für nacheinander abzuarbeitende Tätigkeiten nutzen. Das nächste then() bekommt das Ergebnis des vorhergehenden reingereicht und nicht das Ergebnis des Promises. Der Verwender kann das nutzen, wenn er meint, in seinem Fall etwas verketten zu müssen. Du brauchst das nicht weiter zu berücksichtigen, wenn du das Promise für den Dialog erstellst.

              Eine Verbesserung könnte sein: In deinem Code prüfst du, ob die Callbacks Funktionen sind, bevor du sie aufrufst. Das kann beim Promise wegfallen, weil der Callback im then() angegeben wird. Wenn der Verwender da was Komisches angibt, dann ignoriert das Promise das einfach, ohne einen Fehler zu werfen (ist so definiert). Es ist ja sowieso sein Problem, was der Verwender mit dem Ergebnis anstellt, und wenn er damit Mist macht, muss du das nicht weiter beachten noch dem vorbeugen.

              dedlfix.

              1. Lieber dedlfix,

                Ist das so richtig?

                Ja.

                juhu! Etwas über Promise gelernt!

                Was ist der Unterschied zwischen Cancel und Abort?

                Frage: Sind Sie ein guter Mensch?

                | [X]| | [Ja] [Nein] |

                Hier sehe ich drei Möglichkeiten. Das "Nein" entspricht dem "Abbrechen". Natürlich ist von der natürlich Sprache ein "Abbrechen" mit einem "Schließen" gleich zu setzen. Wenn nun aber die beiden Buttons als Ja/Nein-Frage benutzt werden, ist ein "nein" nicht automatisch auch ein "abgebrochen"...

                Wir wollen das nicht zu einem Tutorial über Promises machen.

                Einverstanden. Der Denkanstoß ist angekommen. Danke dafür.

                Es soll lediglich gezeigt werden, wie durch die Verwendung von Promises der Verwender seinen Code besser strukturieren kann.

                Es sei denn, er baut sich etwas ähnliches, das das auch leistet.

                Eine Verbesserung könnte sein: In deinem Code prüfst du, ob die Callbacks Funktionen sind, bevor du sie aufrufst.

                Du meinst also anstelle von

                dialog.setCallback = function (key, f) { callBacks[key] = f; };

                sollte da besser stehen:

                dialog.setCallback = function (key, f) { if (typeof f == "function") { callBacks[key] = f; } };

                Ja, das könnte ich noch aufnehmen. Aber wer da absichtlich Müll hineinschreibt, dem ist ohnehin nicht zu helfen.

                Liebe Grüße,

                Felix Riesterer.

                1. Tach!

                  Was ist der Unterschied zwischen Cancel und Abort?

                  Frage: Sind Sie ein guter Mensch?

                  | [X]| | [Ja] [Nein] |

                  Hier sehe ich drei Möglichkeiten. Das "Nein" entspricht dem "Abbrechen". Natürlich ist von der natürlich Sprache ein "Abbrechen" mit einem "Schließen" gleich zu setzen. Wenn nun aber die beiden Buttons als Ja/Nein-Frage benutzt werden, ist ein "nein" nicht automatisch auch ein "abgebrochen"...

                  Auch ein Ja schließt den Dialog. Somit kann er durch jede der drei Möglichkeiten geschlossen werden. Das ist also nicht das Kriterium, das zur Entscheidung zwischen resolve und reject führen kann. In dem Fall sehe ich Ja und Nein als gültige Antworten, die resolve() aufrufen sollten. Das Ja gibt in dem Fall dem resolve() dann ein true mit auf den Weg, das Nein ein false. Der X-Button oder Esc oder Klick auf den Backdrop sind keine gültigen Antworten und landen auf reject().

                  Wir wollen das nicht zu einem Tutorial über Promises machen.

                  Einverstanden. Der Denkanstoß ist angekommen. Danke dafür.

                  Es soll lediglich gezeigt werden, wie durch die Verwendung von Promises der Verwender seinen Code besser strukturieren kann.

                  Es sei denn, er baut sich etwas ähnliches, das das auch leistet.

                  Eine Verbesserung könnte sein: In deinem Code prüfst du, ob die Callbacks Funktionen sind, bevor du sie aufrufst.

                  Du meinst also anstelle von

                  dialog.setCallback = function (key, f) { callBacks[key] = f; };

                  sollte da besser stehen:

                  dialog.setCallback = function (key, f) { if (typeof f == "function") { callBacks[key] = f; } };

                  Nein, ich meinte anstelle von

                  if (typeof OK == "function") { dialog.setCallback("ok", function () { OK(); dialog.setCallback("ok", function () {}); }); }

                  braucht es nur

                  dialog.setCallback("ok", function () { resolve(); }); // in dem Fall auch noch kürzer // dialog.setCallback("ok", resolve);

                  Abgesehen davon kann man sich wohl auch im obigen Code das Setzen des Callbacks auf eine Funktion sparen. Zum einen wäre null kürzer und du testest im Dialog-Polyfill sowieso dass der Callback eine Funktion ist. Zum anderen dürfte es auch egal sein, ob da nach dem OK noch irgendwas dem Callback zugewiesen wird, weil eh kein HTML-Element mehr da/sichtbar ist, dass zu einem Aufruf führen könnte.

                  dedlfix.

                  1. Lieber dedlfix,

                    In dem Fall sehe ich Ja und Nein als gültige Antworten, die resolve() aufrufen sollten. Das Ja gibt in dem Fall dem resolve() dann ein true mit auf den Weg, das Nein ein false. Der X-Button oder Esc oder Klick auf den Backdrop sind keine gültigen Antworten und landen auf reject().

                    verstehe. Überzeugt mich. :-)

                    Zum anderen dürfte es auch egal sein, ob da nach dem OK noch irgendwas dem Callback zugewiesen wird, weil eh kein HTML-Element mehr da/sichtbar ist, dass zu einem Aufruf führen könnte.

                    Das habe ich mir auch schon gedacht. Naja, weg kann es immer noch.

                    Liebe Grüße,

                    Felix Riesterer.

                    1. Hallo Ingrid,

                      ich habe die Vorschläge ins Tutorial und in die Live-Beispiele eingearbeitet.

                      Liebe Grüße,

                      Felix Riesterer.

    4. Hallo Felix,

      ich habe zum Thema modale Fenster und JavaScript einen Artikel erstellt: Eigene modale Dialogfenster.

      ich habe den Artikel nur überflogen, ich glaube man kann ihn etwas vereinfachen, indem man mit der Methode HtmlDialogElement.showModal() arbeitet. Ich vermute, dass Browser dann auch das Fokus-Handling übernehmen, hab es aber nicht ausprobiert. Leider ist die Browser-Unterstützung noch unbefriedigend, aber ich glaube da wäre dem Artikel mit einem Hinweis auf einen Polyfill mehr gedient als mit einem handgestrickten Workaround.

      Viele Grüße

      1. Hallo,

        ich habe gerade mal dieses Beispiel auch im Chrome ausprobiert, da wird der Fokus auch nicht gefangen.

        Gruß
        Jürgen

      2. Lieber 1unitedpower,

        ich glaube man kann ihn etwas vereinfachen,

        denke daran, dass die Zielgruppe für das Tutorial zunächst verinnerlichen muss, dass man hier nicht mehr linear programmiert. Das ist das eigentliche Kernthema. Die Sache mit den Dialog-Boxen ist in meinen Augen Semantik-Wichserei mit a11y-Religion. Hat sicherlich beste Gründe, warum man das so machen wollen können sollte, aber vor zehn Jahren hat man sich auch schon zu behelfen gewusst: Mit simplen <div>-Elementen und gleichermaßen passendem CSS - JavaScript natürlich inklusive. Insofern ist an diesem Artikel eigentlich nichts wirklich neues dabei, außer das Gehampel mit Polyfill für schönes <dialog>-Benutzen.

        indem man mit der Methode HtmlDialogElement.showModal() arbeitet. Ich vermute, dass Browser dann auch das Fokus-Handling übernehmen, hab es aber nicht ausprobiert. Leider ist die Browser-Unterstützung noch unbefriedigend,

        Das nenne ich nicht "konstruktive Kritik"! Erst etwas bemängeln, dann zugeben, dass Du es nicht ausprobiert hast und dann noch obendrein zugeben, dass das aufgrund der aktuellen Unterstützung durch die Browser ohnehin noch Zukunftsmusik ist. Nee, konstruktiv geht definitiv anders!

        aber ich glaube da wäre dem Artikel mit einem Hinweis auf einen Polyfill mehr gedient als mit einem handgestrickten Workaround.

        Du darfst gerne den Artikel in Deinem Sinne verbessern. Nimm Dir einfach dazu die notwendige Zeit. Dann kannst Du vorführen, wie es besser geht.

        Auf der anderen Seite musst Du vielleicht überlesen haben, dass ich auf den Wiki-Artikel mit den zugänglichen Dialog-Boxen mehrfach hinweise, in dem die Sache mit der momentanen Notwendigkeit eines Polyfills ja hinlänglich besprochen wird. Dass ich einen "Workaround" gebastelt hätte, verbitte ich mir. Ich spreche konkret von einem Polyfill. Wenn der einmal nicht mehr notwendig sein sollte, kann man alles, was mit dem von mir zur Verfügung gestellten Polyfill nicht mehr gebraucht wird, wegkürzen, um nur noch das Handling mit den Callbacks übrig zu lassen. Darum kommt man so oder so nicht herum - naja, vielleicht doch mit den von @dedlfix schon erwähnten Promises. Aber darauf bist Du hier ja nicht eingegangen.

        Ich erinnere Dich hiermit in aller Deutlichkeit an Deine Kritik an Jörgs Login-System-Tutorial, in der Du auch sehr vage und könnte/müsste/würde formuliert hast. Das macht Deine Kritik wertlos, da sie nicht konkret und damit nicht mehr konstruktiv ist. Auch wenn Du ein in der Sache begründetes Teilanliegen haben magst, entwertest Du durch solche Formulierungsweise in Deiner Kritik das Engagement anderer. Das hat kein Wiki-Autor verdient!

        Wenn Du Dir zur Zeit keine Freizeit abringen kannst, um Wiki-Artikel zu bearbeiten/verbessern oder sogar neue zu schreiben, dann überlege Dir, ob es einen Sinn hat, Deine Zeit in herabwürdigende Kritik zu investieren. Es mag vielleicht sein, dass Du auch hierfür möglicherweise doch keine Zeit hast...

        Liebe Grüße,

        Felix Riesterer.

        1. Das nenne ich nicht "konstruktive Kritik"! Erst etwas bemängeln, dann zugeben, dass Du es nicht ausprobiert hast und dann noch obendrein zugeben, dass das aufgrund der aktuellen Unterstützung durch die Browser ohnehin noch Zukunftsmusik ist. Nee, konstruktiv geht definitiv anders!

          Du hast Recht, ich hätte mich intensiver mit deinem Aritkel beschäftigen müssen und bin dir eine ausführlichere Antwort schuldig. Dass man mit einer Kritik auch ihre Limitierungen benennt, halte ich grundsätzlich für guten Stil, aber nichtsdestotrotz ist meine Kritik zu kurz ausgefallen. Dafür möchte ich mich zunächst bei dir entschuldigen.

          Ich habe den Test von showModal nachgeholt und bei mir wird der Fokus in dem Dialog gefangen. @JürgenB hat bei seinem Test im Nachbar-Thread allerdings etwas anderes beobachtet, dafür habe ich zur Zeit keine Erklärung. Möglicherweise ist das ein Versionssunterschudied, ich habe mit Chrome 74 getestet.

          Auf der anderen Seite musst Du vielleicht überlesen haben, dass ich auf den Wiki-Artikel mit den zugänglichen Dialog-Boxen mehrfach hinweise, in dem die Sache mit der momentanen Notwendigkeit unserer Browser eines Polyfills ja hinlänglich besprochen wird.

          Auch damit bist du im Recht.

          Dass ich einen "Workaround" gebastelt hätte, verbitte ich mir. Ich spreche konkret von einem Polyfill.

          Das war von mir nicht abschätzig gemeint. Workarounds sind ein notwendiges Übel, in deren Entwicklung viel Know-How fließen muss. Polyfills sind auch Workarounds, die dazu dienen (noch) nicht nativ implementierte Features nachzurüsten. Ich hätte das Adjektiv "handgestrickt" aber besser mit einem wertschätzenderen Begriff ausgetauscht.

          Wenn der einmal nicht mehr notwendig sein sollte, kann man alles, was mit dem von mir zur Verfügung gestellten Polyfill nicht mehr gebraucht wird, wegkürzen, um nur noch das Handling mit den Callbacks übrig zu lassen.

          Vorschlag zur Güte: Du hast ja selber vorgeschlagen den Artikel aufzuteilen, ich glaube das Polyfill wäre eine sinnvolle Schnittmarke und könnte in einem eigenen Artikel untergebracht werden.

          Außerdem möchte ich vorschlagen, statt setCallback das native Event-System mit addEventListener zu benutzen. Die Spec definiert für das dialog-Element zwei mögliche Events: close und cancel.

          Dafür müssten im Code des Polyfills die folgenden Zeilen gestrichen werden.

          dialog.setCallback = function (key, f) { callBacks[key] = f; }; dialog.triggerCallback = function (key) { if (typeof callBacks[key] == "function") { callBacks[key](); } };

          Und anstelle von dialog.triggerCallback("cancel") müsste der Aufruf wie folgt lauten dialog.dispatchEvent(new Event('cancel')). Ebenso für das open-Event.

          Auf der aufrufenden Seite, muss der Aufruf dialog.setCallback('cancel', fn) durch dialog.addEventListener('cancel', fn) getauscht werden.

          Ich erinnere Dich hiermit in aller Deutlichkeit an Deine Kritik an Jörgs Login-System-Tutorial, in der Du auch sehr vage und könnte/müsste/würde formuliert hast.

          Die Eskalation damals bedaure ich auch heute noch und habe, wie du ja auch weißt, Jörg dafür ebenfalls um Entschuldigung gebeten. Dass meine Kritik zu vage war, weise ich allerdings zurück. Ich habe mich regelmäßig an den Diskussionen um den Artikel beteiligt, dabei habe ich auf mehrere konkrete Sicherheitslücken hingewiesen. Zum Beispiel die Anfälligkeit für Session-Fixation-Angriffe, der Konflikt mit der Apache Implementierung von HTTP Basic Auth und die Verwendung eines Algorithmus für Pseudozufälle anstelle von kryptografisch sicheren Zufallsgeneratoren. Darüber hinaus habe ich mehrfach und ausführlich die schlechte Lesbarkeit des Codes und die Abwesenheit von Qualitätssicherungs-Maßnahmen bemängelt.

    5. Hallo Ingrid,

      Dieser Artikel [...] wird aber in einer späteren Erweiterung komplexere eigene Dialoge präsentieren.

      [x] done

      Liebe Grüße,

      Felix Riesterer.