loading...

The use for use in Svelte

codechips profile image Ilia Mikhailov Updated on ・3 min read

Recently I stumbled upon this beautiful login form made with Tailwind CSS. It has some Javascript code beside CSS to achieve the desired animation.

Alt Text

The code looks like this.

<style>
    .input {
        transition: border 0.2s ease-in-out;
        min-width: 280px
    }

    .input:focus+.label,
    .input:active+.label,
    .input.filled+.label {
        font-size: .75rem;
        transition: all 0.2s ease-out;
        top: -0.1rem;
        color: #667eea;
    }

    .label {
        transition: all 0.2s ease-out;
        top: 0.4rem;
        left: 0;
    }
</style>

<script>
    var toggleInputContainer = function (input) {
        if (input.value != "") {
            input.classList.add('filled');
        } else {
            input.classList.remove('filled');
        }
    }

    var labels = document.querySelectorAll('.label');
    for (var i = 0; i < labels.length; i++) {
        labels[i].addEventListener('click', function () {
            this.previousElementSibling.focus();
        });
    }

    window.addEventListener("load", function () {
        var inputs = document.getElementsByClassName("input");
        for (var i = 0; i < inputs.length; i++) {
            console.log('looped');
            inputs[i].addEventListener('keyup', function () {
                toggleInputContainer(this);
            });
            toggleInputContainer(inputs[i]);
        }
    });
</script>

<div class="shadow-xl p-10 bg-white max-w-xl rounded">
    <h1 class="text-4xl font-black mb-4">Login</h1>
    <div class="mb-4 relative">
        <input class="removed-for-readability" id="email" type="text">
        <label for="email" class="removed-for-readability">Email Address</label>
    </div>
    <div class="mb-4 relative">
        <input class="removed-for-readability" id="password" type="password">
        <label for="password" class="removed-for-readability">Password</label>
    </div>
    <button class="removed-for-readability">Submit</button>
</div>


I didn't like the fact that you had to reach out to pure DOM functions to achieve this functionality. Turns out that Svelte's use directive is a perfect fit for the job and also a good example of showing one of the things you can use it for. Let's refactor the code a bit.

<style>
  .input {
    transition: border 0.2s ease-in-out;
  }

  .input:focus + .label,
  .input:active + .label,
  .input.filled + .label {
    font-size: 0.75rem;
    transition: all 0.2s ease-out;
    top: -0.1rem;
    color: #667eea;
  }

  .label {
    transition: all 0.2s ease-out;
    top: 0.4rem;
    left: 0;
  }
</style>

<script>
  const labelToggle = node => {
    const handleKey = event => {
      if (event.target.value) {
        event.target.classList.add('filled');
      } else {
        event.target.classList.remove('filled');
      }
    };
    node.addEventListener('keyup', handleKey);

    return {
      destroy() {
        node.removeEventListener('keyup', handleKey);
      }
    };
  };

  const labelClick = node => {
    const click = event => {
      event.target.previousElementSibling.focus();
    };
    node.addEventListener('click', click);

    return {
      destroy() {
        node.removeEventListener('click', click);
      }
    };
  };
</script>

<div class="max-w-lg p-10 bg-white rounded shadow-md">
  <h1 class="mb-4 text-3xl font-black">Login</h1>
  <form>
    <div class="relative mb-4">
      <input use:labelToggle class="removed-for-readability" id="email" type="text" />
      <label use:labelClick for="email" class="removed-for-readability">Email</label>
    </div>
    <div class="relative mb-4">
      <input use:labelToggle class="removed-for-readability" />
      <label use:labelClick for="password" class="removed-for-readability">Password</label>
    </div>
    <div class="text-center">
      <button class="removed-for-readability">Continue</button>
    </div>
  </form>
</div>


Do you notice that our text inputs now have use:labelToggle directives and our labels have use:labelClick? Basically, we have defined the two "use" handlers, or actions as they are called in Svelte, in the script section of the file and then attached them to the appropriate html nodes. But how does it work?

The use directive explained aka Svelte action

The actions are custom code that will be run when the element is mounted on the DOM and will pass the element to that action as a raw DOM node. If the function returns an object with destroy function on it, Svelte will run that function when the element is unmounted from the DOM. Very simple, but also incredibly powerful in case you want to do something outside of Svelte and use the full power of DOM.

Below is an annotated example of the toggle handler attached to our text inputs.


// Svelte passes in raw html DOM element when element is mounted on the DOM
const labelToggle = node => {
    // Define a custom event handler for the text input element
    const handleKey = event => {
      // if element's value is not empty add class "filled"
      if (event.target.value) {
        event.target.classList.add('filled');
      } else {
        event.target.classList.remove('filled');
      }
    };
    // bind custom event handler to element's keyup event
    node.addEventListener('keyup', handleKey);

    // when element is unmounted from the DOM remove the event listener
    return {
      destroy() {
        node.removeEventListener('keyup', handleKey);
      }
    };
  };

You can also pass in parameters to actions and run custom code if parameters change, but I wanted to keep the example simple here. Read the docs if you want to learn more.

Svelte's actions have many use cases,like drag-and-drop, tooltips, etc. Only your imagination is the limit.

Posted on by:

codechips profile

Ilia Mikhailov

@codechips

CTO. Maker. Friend. Manager by day, hacker by night. Svelte.js fan. #rxjs #firebase #js

Discussion

pic
Editor guide