events – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 13 Dec 2022 23:03:50 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 events – CSS-Tricks https://css-tricks.com 32 32 45537868 Holiday Snowtacular 2022 https://css-tricks.com/holiday-snowtacular-2022/ https://css-tricks.com/holiday-snowtacular-2022/#respond Tue, 13 Dec 2022 23:03:49 +0000 https://css-tricks.com/?p=376229 We’ve got ourselves a real holiday treat! Join host Alex Trost from the Frontend Horse community for the Holiday Snowtacular 2022 this Friday, December 16.

There’s a lineup of 12 awesome speakers — including Chris Coyier, Cassidy Williams, Kevin …


Holiday Snowtacular 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’ve got ourselves a real holiday treat! Join host Alex Trost from the Frontend Horse community for the Holiday Snowtacular 2022 this Friday, December 16.

There’s a lineup of 12 awesome speakers — including Chris Coyier, Cassidy Williams, Kevin Powell, and Angie Jones — each discussing various front-end and web dev topics. It’s like the 12 days of Christmas, but wrapped up in a four-hour session for web nerds like us.

It’s a real good cause, too. The event is free, but includes fundraising Doctors Without Borders with a goal of reaching $20,000. You can donate here any time and anything you give will be matched by the event’s sponors. So, come for the front-end fun and help a great cause in the process.

To Shared LinkPermalink on CSS-Tricks


Holiday Snowtacular 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/holiday-snowtacular-2022/feed/ 0 376229
Using AbortController as an Alternative for Removing Event Listeners https://css-tricks.com/using-abortcontroller-as-an-alternative-for-removing-event-listeners/ https://css-tricks.com/using-abortcontroller-as-an-alternative-for-removing-event-listeners/#comments Mon, 15 Feb 2021 21:48:33 +0000 https://css-tricks.com/?p=334169 The idea of an “abortable” fetch came to life in 2017 when AbortController was released. That gives us a way to bail on an API request initiated by fetch() — even multiple calls — whenever we want.

Here’s a super …


Using AbortController as an Alternative for Removing Event Listeners originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The idea of an “abortable” fetch came to life in 2017 when AbortController was released. That gives us a way to bail on an API request initiated by fetch() — even multiple calls — whenever we want.

Here’s a super simple example using AbortController to cancel a fetch() request:

const controller = new AbortController();
const res = fetch('/', { signal: controller.signal });
controller.abort();
console.log(res); // => Promise(rejected): "DOMException: The user aborted a request"

You can really see its value when used for a modern interface of setTimeout. This way, making a fetch timeout after, say 10 seconds, is pretty straightforward:

function timeout(duration, signal) {
  return new Promise((resolve, reject) => {
    const handle = setTimeout(resolve, duration);
    signal?.addEventListener('abort', e => {
      clearTimeout(handle);
      reject(new Error('aborted'));
    });
  });
}

// Usage
const controller = new AbortController();
const promise = timeout(10000, controller.signal);
controller.abort();
console.log(promise); // => Promise(rejected): "Error: aborted"

But the big news is that addEventListener now accepts an Abort Signal as of Chrome 88. What’s cool about that? It can be used as an alternate of removeEventListener:

const controller = new AbortController();
eventTarget.addEventListener('event-type', handler, { signal: controller.signal });
controller.abort();

What’s even cooler than that? Well, because AbortController is capable of aborting multiple cancelable requests at once, it streamlines the process of removing multiple listeners in one fell swoop. I’ve already found it particularly useful for drag and drop.

Here’s how I would have written a drag and drop script without AbortController, relying two removeEventListener instances to wipe out two different events:

// With removeEventListener
el.addEventListener('mousedown', e => {
  if (e.buttons !== 1) return;

  const onMousemove = e => {
    if (e.buttons !== 1) return;
    /* work */
  }

  const onMouseup = e => {
    if (e.buttons & 1) return;
    window.removeEventListener('mousemove', onMousemove);
    window.removeEventListener('mouseup', onMouseup);
  }

  window.addEventListener('mousemove', onMousemove);
  window.addEventListener('mouseup', onMouseup); // Can’t use `once: true` here because we want to remove the event only when primary button is up
});

With the latest update, addEventListener accepts the signal property as its second argument, allowing us to call abort() once to stop all event listeners when they’re no longer needed:

// With AbortController
el.addEventListener('mousedown', e => {
  if (e.buttons !== 1) return;

  const controller = new AbortController();

  window.addEventListener('mousemove', e => {
    if (e.buttons !== 1) return;
    /* work */
  }, { signal: controller.signal });

  window.addEventListener('mouseup', e => {
    if (e.buttons & 1) return;
    controller.abort();
  }, { signal: controller.signal });
});

Again, Chrome 88 is currently the only place where addEventListener officially accepts an AbortSignal. While other major browsers, including Firefox and Safari, support AbortController, integrating its signal with addEventListener is a no go at the moment… and there are no signals (pun sorta intended) that they plan to work on it. That said, a polyfill is available.


Using AbortController as an Alternative for Removing Event Listeners originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/using-abortcontroller-as-an-alternative-for-removing-event-listeners/feed/ 10 334169
The Thinking Behind Simplifying Event Handlers https://css-tricks.com/the-thinking-behind-simplifying-event-handlers/ https://css-tricks.com/the-thinking-behind-simplifying-event-handlers/#comments Thu, 09 May 2019 14:24:07 +0000 http://css-tricks.com/?p=286888 Events are used to respond when a user clicks somewhere, focuses on a link with their keyboard, and changes the text in a form. When I first started learning JavaScript, I wrote complicated event listeners. More recently, I’ve learned how …


The Thinking Behind Simplifying Event Handlers originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Events are used to respond when a user clicks somewhere, focuses on a link with their keyboard, and changes the text in a form. When I first started learning JavaScript, I wrote complicated event listeners. More recently, I’ve learned how to reduce both the amount of code I write and the number of listeners I need.

Let’s start with a simple example: a few draggable boxes. We want to show the user which colored box they dragged.

<section>
  <div id="red" draggable="true">
    <span>R</span>
  </div>
  <div id="yellow" draggable="true">
    <span>Y</span>
  </div>
  <div id="green" draggable="true">
    <span>G</span>
  </div>
</section>

<p id="dragged">Drag a box</p>

See the Pen
Dragstart events
by Tiger Oakes (@NotWoods)
on CodePen.

The intuitive way to do it

I wrote separate event listener functions for each element when I first started learning about JavaScript events. It’s a common pattern because it’s the simplest way to start. We want specific behavior for each element, so we can use specific code for each.

document.querySelector('#red').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged red';
});

document.querySelector('#yellow').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged yellow';
});

document.querySelector('#green').addEventListener('dragstart', evt => {
  document.querySelector('#dragged').textContent = 'Dragged green';
});

Reducing duplicate code

The event listeners in that example are all very similar: each function displays some text. This duplicate code can be collapsed into a helper function.

function preview(color) {
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
}

document
  .querySelector('#red')
  .addEventListener('dragstart', evt => preview('red'));
document
  .querySelector('#yellow')
  .addEventListener('dragstart', evt => preview('yellow'));
document
  .querySelector('#green')
  .addEventListener('dragstart', evt => preview('green'));

This is much cleaner, but it still requires multiple functions and event listeners.

Taking advantage of the Event object

The Event object is the key to simplifying listeners. When an event listener is called, it also sends an Event object as the first argument. This object has some data to describe the event that occurred, such as the time the event happened. To simplify our code, we can use the evt.currentTarget property where currentTarget refers to the element that the event listener is attached to. In our example, it will be one of the three colored boxes.

const preview = evt => {
  const color = evt.currentTarget.id;
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
};

document.querySelector('#red').addEventListener('dragstart', preview);
document.querySelector('#yellow').addEventListener('dragstart', preview);
document.querySelector('#green').addEventListener('dragstart', preview);

Now there is only one function instead of four. We can re-use the exact same function as an event listener and evt.currentTarget.id will have a different value depending on the element that fires the event.

Using bubbling

One final change is to reduce the number of lines in our code. Rather than attaching an event listener to each box, we can attach a single event listener to the <section> element that contains all the colored boxes.

An event starts off at the element where the event originated (one of the boxes) when it is fired. However, it won’t stop there. The browser goes to each parent of that element, calling any event listeners on them This will continue until the root of the document is reached (the <body> tag in HTML). This process is called “bubbling” because the event rises through the document tree like a bubble.

Attaching an event listener to the section will cause the focus event to bubble from the colored box that was dragged up to the parent element. We can also take advantage of the evt.target property, which contains the element that fired the event (one of the boxes) rather than the element that the event listener is attached to (the <section> element).

const preview = evt => {
  const color = evt.target.id;
  document.querySelector('#dragged').textContent = `Dragged ${color}`;
};

document.querySelector('section').addEventListener('dragstart', preview);

Now we’ve reduced many event listeners to just one! With more complicated code, the effect will be greater. By utilizing the Event object and bubbling, we can tame JavaScript events and simplify code for event handlers.

What about click events?

evt.target works great with events like dragstart and change, where there are only a small number of elements that can receive focus or have input changed.

However, we usually want to listen for click events so we can respond to a user clicking on a button in an application. click events fire for any element in the document, from large divs to small spans.

Let’s take our draggable color boxes and make them clickable instead.

<section>
  <div id="red" draggable="true">
    <span>R</span>
  </div>
  <div id="yellow" draggable="true">
    <span>Y</span>
  </div>
  <div id="green" draggable="true">
    <span>G</span>
  </div>
</section>

<p id="clicked">Clicked a box</p>
const preview = evt => {
  const color = evt.target.id;
  document.querySelector('#clicked').textContent = `Clicked ${color}`;
};

document.querySelector('section').addEventListener('click', preview);

See the Pen
Click events: Not quite working
by Tiger Oakes (@NotWoods)
on CodePen.

When testing this code, notice that sometimes nothing is appended to “Clicked” instead of when clicking on a box. The reason that it doesn’t work is that each box contains a <span> element that can be clicked instead of the draggable <div> element. Since the spans don’t have a set ID, the evt.target.id property is an empty string.

We only care about the colored boxes in our code. If we click somewhere inside a box, we need to find the parent box element. We can use element.closest() to find the parent closest to the clicked element.

const preview = evt => {
  const element = evt.target.closest('div[draggable]');
  if (element != null) {
    const color = element.id;
    document.querySelector('#clicked').textContent = `Clicked ${color}`;
  }
};

See the Pen
Click events: Using .closest
by Tiger Oakes (@NotWoods)
on CodePen.

Now we can use a single listener for click events! If element.closest() returns null, that means the user clicked somewhere outside of a colored box and we should ignore the event.

More examples

Here are some additional examples to demonstrate how to take advantage of a single event listener.

Lists

A common pattern is to have a list of items that can be interacted with, where new items are inserted dynamically with JavaScript. If we have event listeners attached to each item, then y\our code has to deal with event listeners every time a new element is generated.

<div id="buttons-container"></div>
<button id="add">Add new button</button>
let buttonCounter = 0;
document.querySelector('#add').addEventListener('click', evt => {
  const newButton = document.createElement('button');
  newButton.textContent = buttonCounter;
  
  // Make a new event listener every time "Add new button" is clicked
  newButton.addEventListener('click', evt => {

    // When clicked, log the clicked button's number.
    document.querySelector('#clicked').textContent = `Clicked button #${newButton.textContent}`;
  });

  buttonCounter++;

  const container = document.querySelector('#buttons-container');
  container.appendChild(newButton);
});

See the Pen
Lists: no bubbling
by Tiger Oakes (@NotWoods)
on CodePen.

By taking advantage of bubbling, we can have a single event listener on the container. If we create many elements in the app, this reduces the number of listeners from n to two.

let buttonCounter = 0;
const container = document.querySelector('#buttons-container');
document.querySelector('#add').addEventListener('click', evt => {
  const newButton = document.createElement('button');
  newButton.dataset.number = buttonCounter;
  buttonCounter++;

  container.appendChild(newButton);
});
container.addEventListener('click', evt => {
  const clickedButton = evt.target.closest('button');
  if (clickedButton != null) {
    // When clicked, log the clicked button's number.
    document.querySelector('#clicked').textContent = `Clicked button #${clickedButton.dataset.number}`;
  }
});

Forms

Perhaps there’s a form with lots of inputs, and we want to collect all the user responses into a single object.

<form>
  <label>Name: <input name="name" type="text"/></label>
  <label>Email: <input name="email" type="email"/></label>
  <label>Password: <input name="password" type="password"/></label>
</form>
<p id="preview"></p>
let responses = {
  name: '',
  email: '',
  password: ''
};

document
  .querySelector('input[name="name"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="name"]');
    responses.name = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });
document
  .querySelector('input[name="email"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="email"]');
    responses.email = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });
document
  .querySelector('input[name="password"]')
  .addEventListener('change', evt => {
    const inputElement = document.querySelector('input[name="password"]');
    responses.password = inputElement.value;
    document.querySelector('#preview').textContent = JSON.stringify(responses);
  });

See the Pen
Forms: no bubbling
by Tiger Oakes (@NotWoods)
on CodePen.

Let’s switch to a single listener on the parent <form> element instead.

let responses = {
  name: '',
  email: '',
  password: ''
};

document.querySelector('form').addEventListener('change', evt => {
  responses[evt.target.name] = evt.target.value;
  document.querySelector('#preview').textContent = JSON.stringify(responses);
});

Conclusion

Now we know how to take advantage of event bubbling and the event object to simplify complex jumbles of event handlers into just a few… and sometimes down to just one! Hopefully this article has helped you think about your event handlers in a new light. I know this was a revelation to me after I’d spent my early development years writing duplicative code to accomplish the same thing.


The Thinking Behind Simplifying Event Handlers originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/the-thinking-behind-simplifying-event-handlers/feed/ 5 286888
Intro to Vue.js: Rendering, Directives, and Events https://css-tricks.com/intro-to-vue-1-rendering-directives-events/ https://css-tricks.com/intro-to-vue-1-rendering-directives-events/#comments Mon, 30 Jan 2017 15:14:51 +0000 http://css-tricks.com/?p=250336 If I was going to sum up my experiences with Vue in a sentence, I’d probably say something like "it's just so reasonable" or "It gives me the tools I want when I want them, and never gets in my way". Again and again when learning Vue, I smiled to myself. It just made sense, elegantly.

This is my own introductory take on Vue. It's the article I wish I had when I was first learning Vue. If you'd like a more non-partisan approach, please visit Vue's very well thought out and easy to follow Guide.


Intro to Vue.js: Rendering, Directives, and Events originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
If I was going to sum up my experiences with Vue in a sentence, I’d probably say something like “it’s just so reasonable” or “It gives me the tools I want when I want them, and never gets in my way”. Again and again, when learning Vue, I smiled to myself. It just made sense, elegantly.

This is my own introductory take on Vue. It’s the article I wish I had when I was first learning Vue. If you’d like a more non-partisan approach, please visit Vue’s very well thought out and easy to follow Guide.

Article Series:

  1. Rendering, Directives, and Events (You are here!)
  2. Components, Props, and Slots
  3. Vue-cli
  4. Vuex
  5. Animations

One of my favorite things about Vue is that it takes all of the successful things from other frameworks, and incorporates them without getting disorganized. Some examples that stand out for me:

  • A virtual DOM with reactive components that offer the View layer only, props and a Redux-like store similar to React.
  • Conditional rendering, and services, similar to Angular.
  • Inspired by Polymer in part in terms of simplicity and performance, Vue offers a similar development style as HTML, styles, and JavaScript are composed in tandem.

Some benefits I’ve enjoyed over Vue’s competitors: cleaner, more semantic API offerings, slightly better performance than React, no use of polyfills like Polymer, and an isolated, less opinionated view than Angular, which is an MVC.

I could go on, but it’s probably better if you read their comprehensive and community-driven comparison with other frameworks. It’s worth a read, but you can skip back to it later if you’d like to dive into the code.

Let’s Get Started!

We can’t kick this off without the obligatory “Hello, world!” example. Let’s do that so you can get up and running:

<div id="app">
 {{ text }} Nice to meet Vue.
</div>
new Vue({
 el: '#app',
 data: {
   text: 'Hello World!'
 }
});

See the Pen Vue Hello World by Sarah Drasner (@sdras) on CodePen.

If you’re familiar with React, this will have some similarities. We’ve escaped into JavaScript in the middle of the content with the mustache template and used a variable, but one difference is we are working with straight up HTML instead of JSX. JSX is pretty easy to work with, but I do think it’s nice that I don’t have to spend time changing class to className, etc. You’ll also notice that this is pretty lightweight to get up and running.

Now let’s try Vue out with something I really love: loops and conditional rendering.

Conditional Rendering

Let’s say I have a set of items, like navigation, that I know I’m going to reuse. It might make sense to put it in an array to update it in a few places dynamically and consistently. In vanilla JS (with Babel) we might do something like this: create the array, then create an empty string where we add each item wrapped in an <li>, and then wrap all of that in a <ul> and add it to the DOM with innerHTML:

<div id="container"></div>
const items = [
  'thingie',
  'another thingie',
  'lots of stuff',
  'yadda yadda'
];

function listOfStuff() {
  let full_list = '';
  for (let i = 0; i < items.length; i++) {
      full_list = full_list + `<li> ${items[i]} </li>`
  }
  const contain = document.querySelector('#container');
  contain.innerHTML = `<ul> ${full_list} </ul>`;     
}

listOfStuff();

See the Pen e699f60b79b90a35401cc2bcbc588159 by Sarah Drasner (@sdras) on CodePen.

This works fine, but it’s a bit messy for something so standard. Now let’s implement that same thing with Vue’s loop with v-for:

<div id="app">
  <ul>
    <li v-for="item in items">
      {{ item }}
    </li>
  </ul>
</div>
const app4 = new Vue({
  el: '#app',
  data: {
    items: [
      'thingie',
      'another thingie',
      'lots of stuff',
      'yadda yadda'
    ]
  }
});

See the Pen Conditional Rendering in Vue by Sarah Drasner (@sdras) on CodePen.

Pretty clean and declarative. If you’re familiar with Angular, this will likely be familiar to you. I find this to be such a clean and legible way to conditionally render. If you jumped into the code and had to update it, you could do so very easily.

Another really nice offering is dynamic binding with v-model. Check this out:

<div id="app">
  <h3>Type here:</h3>
  <textarea v-model="message" class="message" rows="5" maxlength="72"></textarea><br>
  <p class="booktext">{{ message }} </p>
</div>
new Vue({
  el: '#app',
  data() {
    return {
      message: 'This is a good place to type things.'  
    }
  }
});

See the Pen Vue Book v-model basic by Sarah Drasner (@sdras) on CodePen.

You’ll probably notice two things about this demo. First, that it really took nothing at all to type directly into the book and dynamically update the text. Vue enables us to very easily set up two-way binding between the <textarea> and the <p> with v-model.

The other thing you might notice is that we’re now putting data in a function. In this example, it would work without doing so. We could have just put it in an object like our earlier examples. But this would only work for the Vue instance and be exactly the same across the application (thus, not so great for individual components). It’s OK for one Vue instance, but this will share data across all of the child components as well. It’s good practice to start putting data in a function because we’ll need to when we start using components and want them to each hold state of their own.

These aren’t the only easy input bindings available to you at all, and even v-if has an alternate, v-show, which won’t mount/unmount the component, but rather, leave it in the DOM and toggle visibility.

There are so many more directives available to you, here’s a sampling of some of the ones I use very often. A lot of these offer shortcuts as well, so I’ll show both. From here on, we’ll mostly use the shortcuts, so it’s good to at least familiarize yourself with them a little bit in this table.

Name Shortcut Purpose Example
v-if, v-else-if, v-else none Conditional Rendering <g v-if="flourish === 'A'"></g>
<g v-else-if="flourish === 'B'"></g>
<g v-else></g>
v-bind : Bind attributes dynamically, or pass props <div :style="{ background: color }"></div>
v-on @ Attaches an event listener to the element <button @click="fnName"></button>
v-model none Creates two-way binding <textarea rows="5" v-model="message" maxlength="72"></textarea>
v-pre none Skip compiling for raw content, can boost performance <div v-pre>{{ raw content with no methods}}</div>
v-once none Don’t rerender <div class="v-once">Keep me from rerendering</div>
v-show none Will show or hide a component/element based on state, but will leave it in the DOM without unmounting (unlike v-if) <child v-show="showComponent"></child> (toggles visibility when showComponent is true)

There are also really nice event modifiers and other API offerings to speed up development like:

  • @mousemove.stop is comparable to e.stopPropogation()
  • @mousemove.prevent this is like e.preventDefault()
  • @submit.prevent this will no longer reload the page on submission
  • @click.once not to be confused with v-once, this click event will be triggered once.
  • v-model.lazy won’t populate the content automatically, it will wait to bind until an event happens.

You can even configure your own keycodes.

We’ll use these in examples a bit more coming up!

Event Handling

Binding that data is all well and good but only gets us so far without event handling, so let’s cover that next! This is one of my favorite parts. We’ll use the binding and listeners above to listen to DOM events.

There are a few different ways to create usable methods within our application. Just like in vanilla JS, you can pick your function names, but methods are intuitively called, well, methods!

new Vue({
  el: '#app',
  data() {
   return {
    counter: 0
   }
  },
  methods: {
   increment() {
     this.counter++;
   }
  }
});
<div id="app">
  <p><button @click="increment">+</button> {{ counter }}</p>
</div>

We’re creating a method called increment, and you can see that this automatically binds to this and will refer to the data in this instance and component. I love this kind of automatic binding, it’s so nice to not have to console.log to see what this is referring to. We’re using shorthand @click to bind to the click event here.

Methods aren’t the only way to create a custom function. You can also use watch. The main difference is that methods are good for small, synchronous calculations, while watch is helpful with more tasking or asynchronous or expensive operations in response to changing data. I tend to use watch most often with animations.

Let’s go a little further and see how we’d pass in the event itself and do some dynamic style bindings. If you recall in the table above, instead of writing v-bind, you can use the shortcut :, so we can bind pretty easily to style (as well as other attributes) by using :style and passing in state, or :class. There are truly a lot of uses for this kind of binding.

In the example below, we’re using hsl(), in which hue calculated as a circle of degrees of color that wraps all the way around. This is good for our use as it will never fail, so as we track our mouse in pixels across the screen, the background style will update accordingly. We’re using ES6 template literals here.

new Vue({
  el: '#app',
  data() {
    return {
      counter: 0,
      x: 0
    }
  },
  methods: {
    increment() {
      this.counter++;
   },
   decrement() {
     this.counter--;
   },
   xCoordinate(e) {
     this.x = e.clientX;
   }
  }
});
<div id="app" :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }" @mousemove="xCoordinate">
  <p><button @click="increment">+</button> {{ counter }} <button @click="decrement">-</button></p>
  <p>Pixels across: {{ x }}</p>
</div>

See the Pen Showing simple event handling by Sarah Drasner (@sdras) on CodePen.

You can see that we didn’t even need to pass in the event to the @mousemove handler, Vue will automatically pass it for you to be available as a parameter for the method. (shown as e here).

Also, native methods can also be used, such as event.clientX, and it’s simple to pair them with this instances. In the style binding on the element there’s camel casing for hyphenated CSS properties. In this example, you can see how simple and declarative Vue is to work with.

We don’t even actually need to create a method at all, we could also increase the counter directly inline in the component if the event is simple enough:

<div id="app">
  <div class="item">
    <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/backpack.jpg" width="235" height="300"/>
    <div class="quantity">
      <button class="inc" @click="counter > 0 ? counter -= 1 : 0">-</button>
      <span class="quant-text">Quantity: {{ counter }}</span>
      <button class="inc" @click="counter += 1">+</button>
    </div>
    <button class="submit" @click="">Submit</button>
  </div><!--item-->
</div>
new Vue({
  el: '#app',
  data() {
    return {
      counter: 0
    }
  }
});

See the Pen Backpack Shop Counter by Sarah Drasner (@sdras) on CodePen.

You can see that we’re updating the state directly in the @click handler without a method at all- you can also see that we can add a little bit of logic in there as well (as you wouldn’t have lower than zero items on a shopping site). Once this logic gets too complex, though, you sacrifice legibility, so it’s good to move it into a method. It’s nice to have the option for either, though.

Article Series:

  1. Rendering, Directives, and Events (You are here!)
  2. Components, Props, and Slots
  3. Vue-cli
  4. Vuex
  5. Animations

Intro to Vue.js: Rendering, Directives, and Events originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/intro-to-vue-1-rendering-directives-events/feed/ 29 250336
Debouncing and Throttling Explained Through Examples https://css-tricks.com/debouncing-throttling-explained-examples/ https://css-tricks.com/debouncing-throttling-explained-examples/#comments Wed, 06 Apr 2016 19:24:38 +0000 http://css-tricks.com/?p=240286 The following is a guest post by David Corbacho, a front end engineer in London. We’ve broached this topic before, but this time, David is going to drive the concepts home through interactive demos that make things very


Debouncing and Throttling Explained Through Examples originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The following is a guest post by David Corbacho, a front end engineer in London. We’ve broached this topic before, but this time, David is going to drive the concepts home through interactive demos that make things very clear.

Debounce and throttle are two similar (but different!) techniques to control how many times we allow a function to be executed over time.

Having a debounced or throttled version of our function is especially useful when we are attaching the function to a DOM event. Why? Because we are giving ourselves a layer of control between the event and the execution of the function. Remember, we don’t control how often those DOM events are going to be emitted. It can vary.

For example, let’s talk about scroll events. See this example:

See the Pen Scroll events counter by Corbacho (@dcorb) on CodePen.

When scrolling using a trackpad, scroll wheel, or just by dragging a scrollbar can trigger easily 30 events per second. But scrolling slowly (swapping) in a smartphone could trigger as much as 100 events per second during my tests. Is your scroll handler prepared for this rate of execution?

In 2011, an issue popped up on the Twitter website: when you were scrolling down your Twitter feed, it became slow and unresponsive. John Resig published a blog post about the problem where it was explained how bad of an idea it is to directly attach expensive functions to the scroll event.

The suggested solution by John (at that time, five years ago) was a loop running every 250ms, outside of the onScroll event. That way the handler is not coupled to the event. With this simple technique, we can avoid ruining the user experience.

These days there are slightly more sophisticated ways of handling events. Let me introduce you to Debounce, Throttle, and requestAnimationFrame. We’ll also look at the matching use cases.

Debounce

The Debounce technique allow us to “group” multiple sequential calls in a single one.

Imagine you are in an elevator. The doors begin to close, and suddenly another person tries to get on. The elevator doesn’t begin its function to change floors, the doors open again. Now it happens again with another person. The elevator is delaying its function (moving floors), but optimizing its resources.

Try it for yourself. Click or move the mouse on top of the button:

See the Pen Debounce. Trailing by Corbacho (@dcorb) on CodePen.

You can see how sequential fast events are represented by a single debounced event. But if the events are triggered with big gaps, the debouncing doesn’t happen.

Leading edge (or “immediate”)

You may find it irritating that the debouncing event waits before triggering the function execution, until the events stop happening so rapidly. Why not trigger the function execution immediately, so it behaves exactly as the original non-debounced handler? But not fire again until there is a pause in the rapid calls.

You can do this! Here’s an example with the leading flag on:

Example of a “leading” debounce.

In underscore.js, the option is called immediate instead of leading

Try it for yourself:

See the Pen Debounce. Leading by Corbacho (@dcorb) on CodePen.

Debounce Implementations

The first time I saw debounce implemented in JavaScript was in 2009 in this John Hann post (who also coined the term).

Soon after that, Ben Alman created a jQuery plugin (no longer maintained), and a year after, Jeremy Ashkenas added it to underscore.js. It was later added to Lodash, a drop-in alternative to underscore.

The 3 implementations are a bit different internally, but their interface is almost identical.

There was a time that underscore adopted the debounce/throttle implementation from Lodash, after I discovered a bug in the _.debounce function in 2013. Since then, both implementations have grown apart.

Lodash has added more features to its _.debounce and _.throttle functions. The original immediate flag was replaced with leading and trailing options. You can choose one, or both. By default, only the trailing edge is enabled.

The new maxWait option (only in Lodash at the moment) is not covered in this article but it can be very useful. Actually, the throttle function is defined using _.debounce with maxWait, as you see in the lodash source code.

Debounce Examples

Resize Example

When resizing a (desktop) browser window, they can emit many resize events while dragging the resize handle.

See for yourself in this demo:

See the Pen Debounce Resize Event Example by Corbacho (@dcorb) on CodePen.

As you can see, we are using the default trailing option for the resize event, because we are only interested on the final value, after user stops resizing the browser.

keypress on autocomplete form with Ajax request

Why to send Ajax requests to the server every 50ms, when the user is still typing? _.debounce can help us, avoiding extra work, and only send the request when the user stops typing.

Here, it wouldn’t make sense to have the leading flag on. We want to wait to the last letter typed.

See the Pen Debouncing keystrokes Example by Corbacho (@dcorb) on CodePen.

A similar use case would be to wait until user stops typing before validate its input. “Your password is too short” type of messages.

How to use debounce and throttle and common pitfalls

It can be tempting to build your own debounce/throttle function, or copy it from some random blog post. My recommendation is to use underscore or Lodash directly. If you only need the _.debounce and _.throttle functions, you can use Lodash custom builder to output a custom 2KB minified library. Build it with this simple command:

npm i -g lodash-cli
lodash include = debounce, throttle

That said, most use the modular form `lodash/throttle` and `lodash/debounce` or `lodash.throttle` and `lodash.debounce` packages with webpack/browserify/rollup.

A common pitfall is to call the _.debounce function more than once:

// WRONG
$(window).on('scroll', function() {
   _.debounce(doSomething, 300); 
});

// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));

Creating a variable for the debounced function will allow us to call the private method debounced_version.cancel(), available in lodash and underscore.js, in case you need it.

var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);

// If you need it
debounced_version.cancel();

Throttle

By using _.throttle, we don’t allow to our function to execute more than once every X milliseconds.

The main difference between this and debouncing is that throttle guarantees the execution of the function regularly, at least every X milliseconds.

The same way than debounce, throttle technique is covered by Ben’s plugin, underscore.js and lodash.

Throttling Examples

Infinite scrolling

A quite common example. The user is scrolling down your infinite-scrolling page. You need to check how far from the bottom the user is. If the user is near the bottom, we should request via Ajax more content and append it to the page.

Here our beloved _.debounce wouldn’t be helpful. It only would trigger only when the user stops scrolling.. and we need to start fetching the content before the user reaches the bottom.
With _.throttle we can warranty that we are checking constantly how far we are from the bottom.

See the Pen Infinite scrolling throttled by Corbacho (@dcorb) on CodePen.

requestAnimationFrame (rAF)

requestAnimationFrame is another way of rate-limiting the execution of a function.

It can be thought as a _.throttle(dosomething, 16). But with a much higher fidelity, since it’s a browser native API that aims for better accuracy.

We can use the rAF API, as an alternative to the throttle function, considering this pros/cons:

Pros

  • Aims for 60fps (frames of 16 ms) but internally will decide the best timing on how to schedule the rendering.
  • Fairly simple and standard API, not changing in the future. Less maintenance.

Cons

  • The start/cancelation of rAFs it’s our responsibility, unlike .debounce or .throttle, where it’s managed internally.
  • If the browser tab is not active, it would not execute. Although for scroll, mouse or keyboard events this doesn’t matter.
  • Although all modern browsers offer rAF, still is not supported in IE9, Opera Mini and old Android. A polyfill would be needed still today.
  • rAF is not supported in node.js, so you can’t use it on the server to throttle filesystem events.

As a rule of thumb, I would use requestAnimationFrame if your JavaScript function is “painting” or animating directly properties, use it at everything that involves re-calculating element positions.

To make Ajax requests, or deciding if adding/removing a class (that could trigger a CSS animation), I would consider _.debounce or _.throttle, where you can set up lower executing rates (200ms for example, instead of 16ms)

If you think that rAF could be implemented inside underscore or lodash, they both have rejected the idea, since it’s a specialized use case, and it’s easy enough to be called directly.

Examples of rAF

I will cover only this example to use requestAnimation frame on scroll, inspired by Paul Lewis article, where he explains step-by-step the logic of this example.

I put it side by side to compare it to _.throttle at 16ms. Giving similar performance, but probably rAF will give you better results on more complex scenarios.

See the Pen Scroll comparison requestAnimationFrame vs throttle by Corbacho (@dcorb) on CodePen.

A more advanced example where I’ve seen this technique is in the library headroom.js, where the logic is decoupled and wrapped inside an object.

Conclusion

Use debounce, throttle and requestAnimationFrame to optimize your event handlers. Each technique is slightly different, but all three of them are useful and complement each other.

In summary:

  • debounce: Grouping a sudden burst of events (like keystrokes) into a single one.
  • throttle: Guaranteeing a constant flow of executions every X milliseconds. Like checking every 200ms your scroll position to trigger a CSS animation.
  • requestAnimationFrame: a throttle alternative. When your function recalculates and renders elements on screen and you want to guarantee smooth changes or animations. Note: no IE9 support.

Debouncing and Throttling Explained Through Examples originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/debouncing-throttling-explained-examples/feed/ 15 240286
JavaScript Event Madness! Capturing *all* events without interference https://css-tricks.com/capturing-all-events/ https://css-tricks.com/capturing-all-events/#comments Mon, 28 Jul 2014 15:05:29 +0000 http://css-tricks.com/?p=176679 The following is a guest post by Matthias Christen and Florian Müller from Ghostlab. Ghostlab is cross-browser cross-device testing software for Mac and PC. One of the things I’m very impressed Ghostlab can do is sync the events from


JavaScript Event Madness! Capturing *all* events without interference originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The following is a guest post by Matthias Christen and Florian Müller from Ghostlab. Ghostlab is cross-browser cross-device testing software for Mac and PC. One of the things I’m very impressed Ghostlab can do is sync the events from one browser to all the others. Scroll one page, the others you are testing scroll. Click somewhere on one page, that same click happens on the others. I asked them about how the heck it does that when there is so much that could interfere. Matthias and Florian explain:

We have been developing Ghostlab, a tool for cross-device and cross-browser testing your websites. In essence, it syncs any number of clients that view a website and makes it easy to go through that site and make sure it works great and looks good on all viewports and platforms. A central component is the replication of events across clients – when the user clicks on a button, scrolls, or enters text into a form field on one client, we have to make sure that the very same thing happens on all others.

Capturing missed events

The client side script component of Ghostlab is listening for all sorts of events that happen, tries to catch them, and replicate them to all the other clients. At some point, we noted that we didn’t quite catch all events. We had to figure out what the problem was, and came up with a solution that allows you to catch any events that happen on your site, no matter how they are handled by any custom JavaScript.

How can it be that you are listening for an event, but don’t catch it? That’s because any event handler has the option of doing several things with an event. You’ll know the ability to prevent the default action usually taken by the browser (preventDefault()). It allows you, for example, to have a link (<a>) for which the user does not go to its href on a click event.

In addition to telling the browser to not do the default action whenever the event occurs, an event handler can also stop the propagation of an event. When an event is triggered on an element, say a link, any event handler that is attached to this specific element will be allowed to handle the event first. After it is done, the event will bubble up until it reaches the document level. Every listener for this event at any parent of the original element will be able to react to the event – that is, unless a lower event handler decides to stop propagation, in which case the event will no longer go further up in the DOM.

Our example 1 demonstrates this. When you click on the inner div (Level 3), the click handler for this element will handle the event first. Unless it prevents propagation, the parent elements (Level 2, Level 1) will afterwards be able to react to the event in order. In case you tick the “Stop Propagation” checkbox, the event handler will prevent further propagation – so, click events on Level 3 will no longer reach Levels 1 and 2, and click events on Level 2 will no longer reach Level 1.

See the Pen Event Propagation Example I by Florian Mueller (@mueflo00) on CodePen.

In example 2, we demonstrate the effect of stopping immediate propagation. This method implicitly stops the bubbling up of the event, so if there were any parent elements, we would observe the same behavior as in example 1. In addition, it also prevents any additional handlers of the same event on the same element from being executed. In our example, we have to click event handlers registered on our element. If we choose to stop immediate propagation, only the first responder will be able to handle the event, and after calling stopImmediatePropagation, no other handler will be called.

See the Pen Event Propagation Example II by Florian Mueller (@mueflo00) on CodePen.

So if you would want to listen to all the events that happen in the DOM, that’s quite difficult. To prevent missing events due to cancelled bubbling up, you’d have to register all the event handlers on every single element of the DOM. And even then, in case a developer chooses to stop immediate propagation, this would only work if you were the first one to register for the event.

If we want to be absolutely sure that we are informed of any event, no matter what its handlers do with it, we have to get in the loop at the very beginning of event registration. For that purpose, we override the addEventListener function of the EventTarget object. The basic idea is simple: every event handler registration will, in the end, call this method. If we override it, we have full control of what happens when any event handler registers.

The original addEventListener function takes an event handler function (the “original event handler”) as its second argument. If we don’t override the addEventListener function, the original event handler function will be called whenever the specified event occurs. Now, in our custom addEventListener function, we simply wrap the original event handler in our own event handler (the “wrapper function”). The wrapper function contains any logic we require, and can ultimately call the original event handler – if we desire to do so.

Example 3 demonstrates this. The three click events attached to the “Level” elements are registered through our custom addEventListener function, so any time a click event occurs on these elements, our wrapper function is called. There, we observe the status of the checkbox – if it is ticked, we simply do not call the original event handler, thereby preventing any click events to trigger any original event handler. A little side note: if you want to make sure that you take control of all events, you have to make sure that you override the addEventListener function before any event is registered.

See the Pen Event Override Example by Florian Mueller (@mueflo00) on CodePen.

While this solution has helped us improve Ghostlab, you may ask yourself what this could be good for? Well, there are several possibilities. We have sketched two possible use cases below – if you can come up with any other, please share!

Possible Application 1: Event Visualizer

There are tools to help you visualize events on your website (we love, for example, VisualEvent Allan Jardine). Using our technique we can quickly implement such a tool ourselves. In our example, for every registered event, we simply draw a little square on top of the element for which the event was registered. On hover, we display the source code of the (original) registered event handler function.

See the Pen Event Visualizer by Florian Mueller (@mueflo00) on CodePen.

Possible Application 2: Event Statistics

Instead of drawing event indicators onto the screen, you can also display that information in another manner. This example shows you a tabular overview of the registered events on any page (given that you inject the code into it), and updates the triggered events in real-time. This can, for example, be helpful when you are experiencing performance issues and are suspecting it might be because there are too many event handlers.

See the Pen Event Override: Stats by Florian Mueller (@mueflo00) on CodePen.


Events are just a small part of the huge complex world of Front End Development. At Vanamco we are excited to bring you tools that help simplify and streamline your processes whilst keeping you up to date with best practice.


JavaScript Event Madness! Capturing *all* events without interference originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/capturing-all-events/feed/ 15 176679