1
\$\begingroup\$

So, my goal with this is to create a simple login widget that does as close to nothing to the global scope as possible. The login mechanism is less important than the encapsulation. The whole script is wrapped in an immediately invoked function expression, no ids are used on elements, document.currentScript is used for identification of the widget, nothing is written to window, and a custom event on the form element is used to communicate with outside scripts.

<form class="login popout">
  <span class="pulltab">Login</span>
  <input type="username" name="username" placeholder="Username"/>
  <input type="password" name="password" placeholder="Password"/>
  <output class="error"></output>
  <button type="submit">Send</button>
  <script>
    (function () {

      var form = document.currentScript.parentElement;

      var error = function (str) {
        form.getElementsByClassName('error')[0].textContent = str;
      }

      form.addEventListener('submit', function (e) {
        e.preventDefault();

        // Data is sent in plaintext, HTTPS is required.
        /*if (window.location.protocol !== 'https:') {
          error("Connection insecure, aborting login. You may be in danger.");
          return false;
        }*/

        // Prep and send the form. Expect a json response.
        var data = new FormData(form);
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'example.com/login')

        // We got something. If it's not an error, send a login event to the
        // form element. Otherwise, send the returned error text to the error span.
        xhr.onload = function(){
          if (xhr.status > 399) {
            error(xhr.response);
          } else {
            form.classList.add('hidden');
            var details = JSON.parse(xhr.response)
            if (details) {
              var login = new CustomEvent('login', {
                detail: details
              })
              form.dispatchEvent(login);
            } else {
              error('Invalid username or password.')
            }
          }

        }

        // Connection error.
        xhr.onerror = function(){
          error("Couldn't contact the server, please try again later.")
        }

        xhr.send(data);
        return false;
      });
      // Adding css handles for making a popup or modal.
      form.getElementsByClassName('pulltab')[0].addEventListener('click', function(){
        form.classList.toggle('opened')
      });
    })();
  </script>
</form>

I wrote this to be compatible with all current-gen browsers as of 2016-05-26. Document.currentScript specifically should cause the most problems for older browsers. Is there any way to tighten this up further? Is this excessive? My motivation was getting a totally reusable and self-reliant widget that doesn't rely on shadow DOM or other black magic, and doesn't care what the rest of the page is doing.

\$\endgroup\$
  • \$\begingroup\$ Just after I posted this, I realized how redundant "modular widget" is... \$\endgroup\$ – Emmett R. May 26 '16 at 20:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.