Tuesday, May 2, 2017

Events for checkboxes and radio buttons



Almost anyone familiar with developing for the Web has worked with events. We use these events to know when things happen in the browser like when a user clicks on something, that they entered data, when the webpage is finished loading or was resized.

Events for user interaction

When a user clicks on a checkbox or radio button the element dispatches a click event. When clicking on a radio button this event does not always indicate a value has changed only that the user has clicked on the element. The click event is also triggered when the user uses their keyboard to select a checkbox or radio button.

When the checked state of a checkbox or radio button changes, due to the click event, then the change event is dispatched. The change is the preferred event to use as an indication that the checked state has changed on an element. Some older browsers dispatch the click event before the checked state changes so that event is not as reliable as the change event.

The sample code below (and here) displays an appropriate message when the user clicks on the checkbox:

<input id="cb10" type="checkbox" value="10"> Ten<br>
<hr>
<div id="output"></div>
<script>
var el = document.getElementById('cb10');
var out = document.getElementById('output');

function changeHandler(event) {
  if (event.target.checked) {
    out.textContent = 'Ten is checked.';
  }
  else {
    out.textContent = 'Ten is not checked.';
  }
}

el.addEventListener('change', changeHandler);
</script>

The Javascript will display the message 'Ten is checked.' or 'Ten is not checked.' based on the current state of the checkbox.

The checked property of an element

The checkbox and radio button DOM elements have a checked property that indicates the current checked state. The checked property is a boolean value.

Within your JavaScript you can set the checked value and the state of the checkbox or radio button will change.

<input id="cb10" type="checkbox" value="10"> Ten<br>

<script>
var el = document.getElementById('cb10');
el.checked = true;
</script>

When you set the checked property the DOM is not changed. The checked attribute is only used to define the initial state of the checked property.

After the DOM is rendered the first time setting the property does not affect the attribute and setting the attribute no longer affects the property.

CSS can use either the property or the attribute

CSS is designed to support both the property value and the attribute by using the :checked selector and the [checked] selector.

:checked is based on the property value and [checked] is based on the existence of the checked attribute.

The following CSS will draw a green box around the checkbox when the checked property is set to true:

input:checked {
  outline: 3px solid green;
}

And this CSS will draw a red box around the checkbox when the checked attribute exists in the DOM:

input[checked] {
  outline: 3px solid red;
}
But avoid the attribute selector
It is best to avoid using the attribute selector since the attribute does not change when the user selects the checkbox. But the :checked selector does recognize anytime the checked property has changed. If you want to manage the attribute yourself, then the attribute selector might work for you.


Setting the checked property does not trigger an event

This is something that is not well understood. But when you set the checked property in JavaScript an event is not dispatched. Which means that if your code expects to always get an event when the checked state of an element has changed you will only get the event when the user interacts with the element and not when the JavaScript changes the property.

This example shows that setting element.checked does not trigger an event. Line 18 sets element.checked to true or false based on the button that was clicked.



My solution


I provide two examples below that allow your JavaScript to emulate the events that are sent by default through user interaction.

The first example sends the click event. Then the browser automatically sends the change event. This acts most like the real user interaction.


function setChecked(el, state) {
  el.checked = !state;
  el.dispatchEvent(new MouseEvent('click'));
}

The second example only triggers the change event. If if all you are listening for is the change event then this might be the better option. It is also slightly faster since only one event is dispatched.


function setChecked(el, state) {
  el.checked = state;
  el.dispatchEvent(new Event('change’));
}

Either example will help to resolve the issue of events not being triggered when setting the checked property. But you have to make sure that all code calls the helper function instead of setting the checked property directly.

In this example I use the setChecked function and events fire like I expect and want.

Tuesday, January 3, 2017

Extended `typeof` in JavaScript


For a long time I have looked better `typeof` functionality in JavaScript. A function that would provide a more real-world answer for any JavaScript variable. Back in November I found Axis written by Todd Motto. At first glance it looked like it would fit my need.

Axis is great. It is less than 1K in size and simple to use. You just call `axis.isString('')` or `axis.isArray([])`. Todd provided `is` functions for all of the ES5 variable types. But after playing with Axis for a few minutes before I found a few bugs and realized that it didn't support ES6 types and I wanted, among other things, to know the type of a variable so I could use it in a switch statement. I liked what Todd had created but I wanted more.

So I created `xto` (Which stands for eXtended TypeOf). It does everything that Axis does but it fixed the few bugs related to `null` and `undefined` and added the features I needed.

With `xto` you can call `xto.isAtring(v)`, `xto.isMap(v)`, `xto.isPromise(v)`, etc. But you can also call `xto.typeof(v)` to get back the type of variable `v`. If you want to know the instance of an object you can call `xto.instance(v)` and if you want to know the entire instance hierarchy you can call `xto.instances(v)` which will return an array of instance strings.

I also added `xto.isAnyArray(v)` and `xto.isTypedArray(v)`. `xto.isAnyArray(v)` returns true if `v` is an `Array`, `Int16Array`, `Float64Array` or any other ES6 typed array. `xto.isTypedArray(v)` returns true is `v` is one of the ES6 typed arrays like `Int8Array`, `UInt32Array`, etc.

`xto` was written to use anywhere. You can `require` it in your Node.js code, `Import` it into Client-side JavaScript or just load it through a `<script>` tag. The minimized file is less than 2K in size. I have hand optimized the code to produce the smallest file possible.

If you need a better way to tell the typeof a JavaScript variable or the instance of a JavaScript object then `xto` is here to help.