Search code examples
javascriptperformancedialogmodal-dialogpolyfills

Avoid repeating javascript for each element


I'm using the Google dialog-polyfill for modals on my site. I need to have multiple modals on a page that can be launched independently. Right now, the only method that I can get to work is to repeat all of the javascript, which seems completely inefficient:

<dialog id="dialog" name="modal">
    <p>This is the login modal dialog.</p>
    <input type="text"></input>
    <p><button id="close-dialog">Close dialog</button></p>
</dialog>
<p><button id="open-dialog">Open login dialog</button></p>

<dialog id="dialog2" name="modal">
    <p>This is the registration modal dialog.</p>
    <input type="text"></input>
    <p><button id="close-dialog2">Close dialog</button></p>
</dialog>
<p><button id="open-dialog2">Open registration dialog</button></p>

<script>
    var dialog = document.getElementById('dialog');
    var dialog2 = document.getElementById('dialog2');
    dialogPolyfill.registerDialog(dialog);
    dialogPolyfill.registerDialog(dialog2);

    document.getElementById('open-dialog').addEventListener('click', function () {
        dialog.showModal();     });
    document.getElementById('close-dialog').addEventListener('click', function () {
        dialog.close();     });

    document.getElementById('open-dialog2').addEventListener('click', function () {
        dialog2.showModal();     });
    document.getElementById('close-dialog2').addEventListener('click', function () {
        dialog2.close();     });

    dialog.addEventListener('click', function (event) {
        var rect = dialog.getBoundingClientRect();
        var isInDialog = (rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width);
        if (!isInDialog) {
            dialog.close();
        }
    });

    dialog2.addEventListener('click', function (event) {
        var rect = dialog2.getBoundingClientRect();
        var isInDialog = (rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width);
        if (!isInDialog) {
            dialog2.close();
        }
    });
</script>

I've played around with this and adding name attributes to no avail:

    var names = document.getElementsByName("modal");
    for (var i = 0; i < names.length; i++) {
        names[i].onchange = function () {
            document.getElementById('open-dialog').addEventListener('click', function () {
                dialog.showModal();
            });
            document.getElementById('close-dialog').addEventListener('click', function () {
                dialog.close();
            });
        }
    }

I don't see how classes would solve this. Is there a way to condense the code so that I won't need to repeat it for each and every modal dialog?


Solution

  • I edited your fiddle:

    http://jsfiddle.net/178f81qc/2/

    Is this what you mean?

    HTML

    <div class="dialog-container">
        <dialog>This is dialog one <button class="close">close</button></dialog>
    
        <button class="open">open dialog one</button>
    </div>
    
    <br>
    
    <div class="dialog-container">
        <dialog>This is dialog two <button class="close">close</button></dialog>
    
        <button class="open">open dialog two</button>
    </div>
    

    Javascript

    var dialogContainers = document.querySelectorAll('.dialog-container'), i;
    for (i=0; i<dialogContainers.length; i++) {
    
        var dialog = dialogContainers[i].querySelector('dialog'), // the <dialog> element
            btnOpen = dialogContainers[i].querySelector('.open'),  // the open button
            btnClose = dialogContainers[i].querySelector('.close'); // the close button
    
        // for old browsers
        dialogPolyfill.registerDialog(dialogContainers[i]);
    
        btnOpen.addEventListener('click', function () {
            this.showModal();
        }.bind(dialog));
    
        // because you bind 'dialog' to the function, 'this' will be the dialog.
        // (if you don't use bind, 'this' will be btnOpen instead)
    
        btnClose.addEventListener('click', function () {
            this.close();
        }.bind(dialog));
    }