The use for use in Svelte

· 3 min

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

Login form screenshot

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="max-w-xl p-10 bg-white rounded shadow-xl">
<h1 class="mb-4 text-4xl font-black">Login</h1>
<div class="relative mb-4">
<input class="removed-for-readability" id="email" type="text">
<label for="email" class="removed-for-readability">Email Address</label>
</div>
<div class="relative mb-4">
<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 actionpermalink

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.


If you liked what you read, it would make me really happy if you could share it on Twitter.