How to trigger a CSS animation on button click

In this tutorial we are going to see how to create a CSS animation and trigger it every time we click on a button. We’re going to use pure JavaScript. We’re not going to use jQuery because it’s not really needed and as we’ve seen in a previous blog post jQuery makes our scripts slower so avoiding it whenever possible will transform our code to be more efficient even if it may take a few more lines to achieve the same thing.

This example’s use case is a pretty common one, you have some data displayed to the user via a grid, a chart or simply an unordered list and that data may change overtime so you want to give the user the change to reload the data again from the server asynchronously. Usually for that kind of task you will need to employ a user-friendly icon button and not just throw a “Refresh” button. Something like this:

refresh

looks way more slick and increases the UX points of your site. The reason for this is that the user prefers to scan the page instead of reading it so removing unwanted text from action buttons and filters actually makes the browsing experience faster and more pleasant. Adding to that, users find it easier to use an interface that seems familiar to them. A refresh button like the one above is something that the user sees and utilizes in a regular basis in other applications like the operating system (especially mobile OSes) and the browser itself.

Enough talking, let’s get to the code. We’re obviously going to need a button and a refresh icon. We get the icon from FontAwesome (https://fontawesome.io/icons/) and style (using Sass of course) the button a bit to remove borders, make its background-color transparent, put a nice color to the refresh button and change the cursor to the “hand pointer” when the user hovers over the button.

Ignore the animation stuff for now and focus on the icon-btn class used to render the button. We’ll get to the animation mixins shortly

When it comes to animations that need to support a variety of browser versions we tend to get lost in the chaos of vendor specific prefixed but Sass, as always, comes to the rescue. To create the animation we use the @keyframes keyword and state that we want our animation to rotate the icon 360 degrees.The @-webkit- prefix is used to ensure Safari 4.0-8.0 compatibility.

Then we need a class that will apply the animation to the icon, let’s name it ‘spin-animation’. Every time we apply this class to the icon the icon will spin. We need to provide 3 details to the browser’s animation engine:
– The animation name, spin
– The animation duration, say, 0.5 seconds,
– The animation timing function. For this example we set it to ‘ease-in’ so our animation starts slowly and then builds up speed fast.

To include all possible vendor prefixed we create a mixin for each of these properties, animation-name mixin, animation-duration mixin and animation-timing-function mixin. We also create one fourth mixin that takes three parameters, the animation name, the animation duration and the animation-timing-function and passes them to the other three mixins. That way our code becomes extremely clean because we only need to include one mixin to our .spin-animation class in order for it to work as expected.

Now to the JavaScript part. Check the updated fiddle below that also presents the JavaScript code:

 

 

Every time the user clicks on the button, the icon should spin. To do that we listen for the button’s ‘click’ event and add the ‘spin-animation’ class to the icon. If we were to stop at this point, and not include the call to setTimeout, the code would actually work. The icon would spin on button click. But it would spin only once. The next click wouldn’t cause the button to spin. You can simulate this behavior by commenting out the call to setTimeout in the above fiddle. The reason why the second (and the third, and the fourth…) click fails is because the class is already set and the animation iteration count is by default set to 1 (Setting it to infinite would cause the button to spin eternally). So what do we do? We remove it after the animation is complete!. setTimeout ensures that upon animation completion after 500ms, which can be translated to 0.5s which is the animation-duration, the spin-animation class will be removed from the icon thus allowing the button to spin when the class is added again in the future.

That’s how you can work with animations that need to run for a finite number of iterations when triggered by some DOM event. That’s all for today! Bye!

Object Property Descriptors in JavaScript

If you’ve ever used JavaScript there is a 99.9999999% change that you’ve used objects. That’s obvious. What is not that obvious is that there is more to an object and its properties than what meets the eye. ECMAScript 2015 introduced the concept of property descriptors. A property descriptor is essentially a set of property metadata that describes how the property should behave. You can access a property’s descriptor using the Object.getOwnPropertyDescriptor function. You can define a new property along with its descriptor or modify an already existing property’s descriptor with Object.defineProperty. Let’s see descriptors in action:

var obj = {
a: 1,
b: 2,
c: 3
};

console.log(Object.getOwnPropertyDescriptor(obj, “a”));

This prints the property’s descriptor in our console window:

blog1

As you can see a property’s descriptor is itself an object with 4 properties. The obvious one is the value which is, like the name implies, the value set to the property (in this example obj.a).

The other three are the most important and the ones in need of further discussion. All you need to know is summarized in the following lists:

(defineProperty can be used to define a new property along with its metadata or modify an existing one provided that its descriptor’s configurable property is set to true)

Writable

  •  If set to true the value can be changed
  •  If set to false the value cannot be changed
    •  In non-strict mode any attempt to change the property’s value will fail silently
    •  In strict-mode any attempt to change the property’s value will trigger a TypeError

Let’s see some examples

var obj = {};

//We define a new property called 'foo' in obj with 'writable' explicitly set to true
Object.defineProperty(obj, "foo", {
	value: 100,
    writable: true
});

console.log(obj.foo);	//100;
//foo's value can be changed
obj.foo =  "foo";
console.log(obj.foo);	//'foo'

When we explicitly set ‘writable’ to false, the property’s value cannot be changed after the initial assignment.
The first example runs in non-strict mode while the second one is in strict mode and therefore the assignment of property ‘foo’ results in an error

Example #1: Non Strict Mode

var obj = {};

//We define a new property called 'foo' in obj with 'writable' explicitly set to false
Object.defineProperty(obj, "foo", {
    value: 100,
    writable: false
});

console.log(obj.foo);	//100;
//Assignment fails silently
obj.foo =  "foo";
console.log(obj.foo);	//100

Example #2: Strict Mode

"use strict"

var obj = {};

//We define a new property called 'foo' in obj with 'writable' explicitly set to false
Object.defineProperty(obj, "foo", {
	value: 100,
    writable: false
});

console.log(obj.foo);	//100;
//Assignment fails silently
obj.foo =  "foo";
console.log(obj.foo);	//Uncaught TypeError: Cannot assign to read only property 'foo'

Configurable

  • If set to true we can use Object.defineProperty to modify the property’s descriptor
  • If set to false we cannot use Object.defineProperty to modify the property’s descriptor
  • If set to false we cannot use’ delete’ to remove the property.
    •  In strict mode a TypeError is thrown.
    •  In non-strict mode the removal fails silently
  •  Exception: If set to false the only thing allowed to change is the writable status from true to false and not vice versa (TypeError thrown)

Example #3: Setting ‘configurable’ to false and then attempting to change ‘enumerable’ to false

var obj = {
	a: 1,
    b: 2,
    c: 3
};

Object.defineProperty(obj, "a", {
	configurable: false
});

Object.defineProperty(obj, "a", {
	enumerable: false	
});	//Uncaught TypeError: Cannot redefine property: a

Example #4: Trying to remove a non-configurable property using the ‘delete’ keyword (non-strict mode):

var obj = {
	a: 1,
    b: 2,
    c: 3
};

Object.defineProperty(obj, "a", {
	configurable: false
});

console.log(obj);	//{a: 1, b: 2, c: 3}
delete obj.c;
console.log(obj);	//{a: 1, b: 2}
delete obj.a;	//Fails silently
console.log(obj);	//{a: 1, b: 2}	(a is still here)

Example #5: Trying to remove a non-configurable property using the ‘delete’ keyword (strict mode):

"use strict"

var obj = {
	a: 1,
    b: 2,
    c: 3
};

Object.defineProperty(obj, "a", {
	configurable: false
});

console.log(obj);	//{a: 1, b: 2, c: 3}
delete obj.c;
console.log(obj);	//{a: 1, b: 2}
delete obj.a;	//Uncaught TypeError: Cannot delete property 'a'

Example #6: Setting ‘writable’ to false even when ‘configurable’ has already been set to false

Have you noticed that in Example #3 the property we tried to change after setting ‘configurable’ to true is the ‘enumerable’ property even though we haven’t talked about it yet? Well the reason why, is that there is an exception to what cannot be changed once configurable is set to false and that is when ‘writable’ is true. See the next example:

"use strict"

var obj = {
	a: 1,
    b: 2,
    c: 3
};

//We set configurable to false so theoretically we cannot change anything from here on
Object.defineProperty(obj, "b", {
	configurable: false
});

//You would expect an error but this operation completes
Object.defineProperty(obj, "b", {
	writable: false
});

obj.b = 120;	//Oops! Type error

If you run this piece of code you’re going to see that we managed to successfully make ‘b’ immutable even with ‘configurable’ set to false. And this is the one and only exception to the rule. Also note that if we try to change ‘writable’ back to true it won’t work as the exception is not bidirectional

Enumerable

  • If set to true the property is enumerable and will show up in a for .. In loop
  • If set to false the property will not show up in a for..in loop

 

Example #7: Omitting a property from the list of enumerables

function printEverything(obj){
	for(var prop in obj){
    	if(obj.hasOwnProperty(prop)){
        	console.log(prop + ": " + obj[prop]);
        }
    }
}

var obj = {
	a: 1,
    b: 2,
    c: 3
}

printEverything(obj);	//a: 1, b: 2, c: 3
Object.defineProperty(obj, "b", {
	enumerable: false
});
console.log("");
printEverything(obj);	//a: 1, c: 3

The most common use case scenario for modifying property descriptors will probably be when you want to create an immutable property. See the following example:

Example #8: Real life scenario: Immutable Property

var obj = {
	a: 1,
    b: 2
}

//We create a new property in 'obj' setting it to 18. That value cannot be changed. 'enumerable' set to true by default
Object.defineProperty(obj, "c", {
	value: 18,
    writable: false,
    configurable: false
});

console.log(obj);	//{ a: 1, b: 2, c: 18 }
obj.c = 5;
console.log(obj.c);	//18, No error, we're not in strict mode

That’s all for now. I will be coming back with another blog post discussing more stuff about property descriptors and how you can perform common descriptor operations faster with the use of methods like seal, freeze and preventExtensions. Until then.. Keep coding….