Saturday, February 5, 2022

JavaScript Comparison Operators



Comparison Operators

Most JavaScript developers are familiar with the concept of a boolean. A boolean can have a value of true or false.

You can convert anything into a boolean like this:

Boolean(0); // returns false
Boolean("text"); // returns true

You can also calculate a boolean value by using several operators. This is what allows developers to write conditional code. For example, the equation ( 1 > 3 ) returns false. And ( "A" <= "B" ) returns true.

The equality operator "==", the inequality operator "!=", the greater than operator ">", the less than operator "<", the greater than or equals operator ">=" and the less than or equals operator "<= " all compare values and return a boolean value.

The thing to be aware of is that all of these operators also use type coercion in the comparison. This means that if you compare a number to a string, then JavaScript will convert the number to a string and then compare the two string values. As an example, this equation ( "1" == 1 ) returns true. This may be exactly what you want, but it can also lead to potential errors.

Since JavaScript uses type coercion these comparisons are called truthy and falsy comparisons. There are also some values that are, by their nature, considered truthy and falsy. true and false are boolean values. Truthy and falsy are just a way to describe when something is considered true or false when compared with a boolean.

Falsy and Truthy Values

The values false, 0, null, undefined, an empty array [], and empty string "" are all falsy values.

Truthy values include any number other than 0, array with any values, any object, and a string with any character length.

There are some things that may not seem to follow the above rules and some of the following examples may seem confusing. The equation ( "" == false ) returns true, ( "0" == 0 ) returns true, but ( "false" == false ) returns false. This is because type coercion will convert the string "false" into a boolean value and then compare those two boolean values. Remember that a string with any length is coerced to a boolean value of trueThis code Boolean("") returns false and this code Boolean("false") returns true.

JavaScript does have a few more odd behaviors with the comparison operators. For example ( NaN == true ), ( NaN == false ), and ( NaN == NaN ) all return false;

( {} == {} ), ( [] == [] ) both return false. Mainly because each object and each array comparison is really comparing the references to the array or object.

The following example creates an object, assigns it to the global variable a and then passes that object reference into the function isSame().

The function isSame() compares the references of global variable a and the local variable b. This comparison returns true since both a and b reference the same object.

let a = {};
function isSame(b) {
   return a == b;

isSame(a); // returns true

To demonstrate how odd NaN is, the example right above does not work with NaN. If you change the first line to let a = NaN; the call to isSame(a)returns false. To get the function isSame() to work with NaN you would have to change it like this:

function isSame(b) {
  if(isNaN(a) && isNaN(b)) {
    return true;
  }

  return a == b;

The function isNaN() is used to check if a value is Not a Number.

Strict comparison operators

If you want to avoid type coercion you need to use the strict comparison operators. These operators are the strict equality operator "=== " and the strict inequality operator "!==".

There are no strict versions of the operators greater than, less than, greater than or equals, and less than or equals.

While the equation ( "1" == 1 ) returns true due to type coercion, the equation ( "1" === 1 ) returns false since the two values are different types.

There is a strict version of isNaN. The function Number.isNaN(val) will check to see if val is equal to NaN and that val's type is number.

Calling isNaN("HaHa") returns true but the function Number.isNaN("HaHa") returns false since a string was passed in. Both isNaN(NaN) and Number.isNaN(NaN) return true.



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.

Thursday, January 22, 2015

How to determine the number of bindings in Angular


Angular Uses Bindings

Anyone using Angular should already know that.

Depending on the size of your app you might have many bindings. You may even have too many bindings. The magic number I keep reading about is around 2000 bindings for a given app. Anything above that tends to slow down processing of the app.

How many bindings you are using?

The simplest and least accurate method would be to count the number of bindings you see in your template and multiply it by the number of repeated objects you have on the page. Doing this you are likely to get a number that is a huge underestimation.

Instead you can take the code below and paste it into the console of your preferred web browser. You must have Underscore.js or Lo-Dash loaded.


function getScopeList(rs) {
    var scopeList = [];
    function traverseScope(s) {
        scopeList.push(s);
        if (s.$$nextSibling) {
            traverseScope(s.$$nextSibling);
        }
        if (s.$$childHead) {
            traverseScope(s.$$childHead);
        }
    }
    traverseScope(rs);
    return scopeList;
}

scopes = getScopeList(angular.element(document.querySelectorAll("[ng-app]")).scope());
total = _.uniq(_.flatten(scopes.map(function(s) { return s.$$watchers; }))).length;


This will give you the total number of scopes for ONE ng-app. For many web app that is all you will have.

But I have bootstrapped more than one ng-app...

Some pages have more than one ng-app. While I don't recommend this it, sometimes, needs to happen. If this is your case then you will want to paste the code below into the browser console:


function getScopeList(rs) {
  var scopeList = [];

  function traverseScope(s) {
    scopeList.push(s);
    if (s.$$nextSibling) {
      traverseScope(s.$$nextSibling);
    }
    if (s.$$childHead) {
      traverseScope(s.$$childHead);
    }
  }

  traverseScope(rs);
  return scopeList;
}

total = 0;
apps = angular.element(document.querySelectorAll("[ng-app]"));
[].forEach.call(apps, function(app,i) {
  ngapp = angular.element(app);
  slist = getScopeList(ngapp.scope());
  wl = slist.map(function(s) {return s.$$watchers;});
  c = _.uniq(_.flatten(wl)).length;
  appName = ngapp.attr("ng-app") || ngapp.attr("data-ng-app") || i;
  console.log("App %s: %d - Scopes: %O", appName, c, slist);
  total += c;
});
total


This will output a separate value for each ng-app that you have running and then the total for all ng-apps. The output also displays the name of the app by reading the attribute ng-app from the DOM element that defined it. And the last element displayed is the array of scopes that are part of that app.

The second set of code works for both single ng-apps or multiple ng-apps.

That's it

Now you can see how many bindings and scopes each of your ng-apps have.

Update - Feb 2, 2015
I apologize for the incorrect code examples above prior to Feb 2. I had taken my functioning code and, in an attempt to make the function names a little more explicit I broke the examples. Yes, I know, I should have tried them again before I posted, but I got lazy.

The code has been corrected and now works like it should.

Monday, January 19, 2015

Directive iv-on-cmd - Simplifying Angular click events handling

iv-on-cmd is an Angular directive that allows for a single event handler to handle click events for all child elements of the element containing the directive iv-on-cmd as long as the child elements have the attribute data-cmd set.

The goal ofiv-on-cmd is to reduce the number of ng-click handlers and provide a single common function to handle all of the children.

Backstory

I have discovered that Angular tends to slow down with too many bindings and I have read articles that indicate that as few as two-thousand bindings can be too many bindings.

I have had several projects where the result of an ng-repeat created several hundred to several thousand DOM elements. And each of these elements had between three and thirty bindings. This was way more than the estimated slow-down limit of two thousand.

I needed a way to do something I used to do with jQuery. And that is use a single click handler on a wrapper <div> that tells me the command to execute for all of the wrapper's children. Using jQuery I would do the following:

<div class="main-wrapper">
  <button class="start-process">Start</button>
  <button class="cancel">cancel</button>
</div>

The above is a greatly simplified DOM, but it works for this explanation.

With the example HTML above I would add some jQuery to process each <button> independently:

function startProcess() {
  // Do some kind of process here
}

function cancel() {
  // Cancel the operation here
}

$(document).ready(function() {
  $(".start-process").on("click", startProcessFn);
  $(".cancel").on("click", cancelFn);
});

But when I had hundreds of these buttons, or other DOM elements, it got to be a massive process writing each of these click handlers.
Angular helped improve this by allowing you to set the click handler via the ng-click directive, like this:

<div class="main-wrapper" data-ng-controller="myCtrl">
  <button data-ng-click="startProcess()">Start</button>
  <button data-ng-click="cancel()">cancel</button>
</div>

Then in the controller code I would do this:

angular.module("mine").controller("myCtrl", myCtrl);
myCtrl.$inject = ["$scope"];
function myCtrl($scope) {
  $scope.startProcess = function() {
    // Do some kind of process here
  };

  $scope.cancel = function() {
    // Cancel the operation here
  }
}

But this could still, with hundreds of ng-click directives cause a massive number of bindings to occur.

My jQuery Solution

To resolve the many event bindings in jQuery I create a command handler. This would utilize the delegate version of the $().on() function. This is done by setting the $().on() handler on a parent and specify the children that will cause your code to be called. So with this HTML:

<div class="main-wrapper">
  <button data-cmd="start-process">Start</button>
  <button data-cmd="cancel">cancel</button>
</div>

And the script would look like this:

function processCmd(event) {
  var $el = $(event.target);
  var cmd = $el.data("cmd");

  console.log("Command was %s", cmd); 

  // Process the commands.
  switch(cmd) {
    case "start-process":
      // Do something
      break;

    case "cancel":
      // Do something
      break;
  }
}

$(document).ready(function() {
  $(".main-wrapper").on("click", "[data-cmd]", processCmd);
});

With this code the click handler is a delegated handler. Meaning that when the user clicks on the <button> the event is delegated to the event handler connected to the <div> tag. Now, even with hundreds of buttons, I only have one event handler. And, if buttons are added later, my event handler is still called.

The example above is small enough that what I am describing may not make sense. But imagine having something that is repeated and the only difference between each of them is an index value or some form of a key value. Take a web-based message application as an example. Each message has it's own unique identifier. If each message had a read button and a delete button then you would need two event handlers per message. But using the delegate for of $().on() we can have one event handler that handles all of the messages.

<div class="mail-shell">
  <div class="message">
    <span class="sender">someone@example.com</span>
    <span class="subject">Some message subject</span>
    <span class="time">3:43 am</span>
    <span><button data-cmd="read" data-cmd-data="KE1R-DJ5KW-9SJ21">Read</button></span>
    <span><button data-cmd="delete" data-cmd-data="KE1R-DJ5KW-9SJ21">Delete</button></span>
  </div>
  <div class="message">
    <span class="sender">person@example.com</span>
    <span class="subject">Buy something from us</span>
    <span class="time">2:49 am</span>
    <span><button data-cmd="read" data-cmd-data="K19D-0PWR8-MMK92">Read</button></span>
    <span><button data-cmd="delete" data-cmd-data="K19D-0PWR8-MMK92">Delete</button></span>
  </div>
  <div class="message">
    <span class="sender">bot@example.com</span>
    <span class="subject">Buy a Rolex from us</span>
    <span class="time">2:31 am</span>
    <span><button data-cmd="read" data-cmd-data="LK0P-HN8GT-00LPD">Read</button></span>
    <span><button data-cmd="delete" data-cmd-data="LK0P-HN8GT-00LPD">Delete</button></span>
  </div>
</div>

Now imagine hundreds of these messages instead of the three in the example above.
Using a few lines of code and only one event handler we can handle all of the click events for all of the buttons, even if a new message show up after we set up our event handler.

function processCmd(event) {
  var $el = $(event.target);
  var cmd = $el.data("cmd");
  var cmdData = $el.data("cmdData");
  switch(cmd) {
    case "read":
      openMessage(cmdData);
      break;

    case "delete":
      deleteMessage(cmdData);
      break;
  }
}

$(document).ready(function() {
  $(".main-wrapper").on("click", "[data-cmd]", processCmd);
});

My Angular Directive: iv-on-cmd

My Angular directive, iv-on-cmd, used the delegate functionality of jQuery to simplify Angular code. It does some of the behind-the-scenes work for you. It figures out what the command cmd is and the command data cmdData and inserts that into the $event.data object. Then it passes $event through to your handler.

The following HTML example has the iv-on-cmd directive on the outer <div>. This allows one event handler processCmd() to handle all of the click events from the three child buttons.

<div data-ng-controller="myCtrl" data-iv-on-cmd="processCmd($event)">
  <button data-cmd="sayHello">Say Hello</button>
  <button data-cmd="speak" data-cmd-data="Hi">Say Hi</button>
  <button data-cmd="speak" data-cmd-data="Bye">Say Bye</button>
</div>

The example controller below supplies the processCmd() function that is to be accessed any time the user clicks on one of the buttons with the data-cmd attribute.

angular.module("mine").controller("myCtrl", myCtrl);
myCtrl.$inject = ["$scope"];
function myCtrl($scope) {
  $scope.processCmd = function($event) {
    $event.stopPropigation();
    $event.preventDefault();
       if ($event.data.cmd === "sayHello") {
         alert("Hello");
         return;
       }

       if ($event.data.cmd === "speak" ) {
           alert("Speaking: " + $event.data.cmdData);
           return;
       }
  }
}

In the buttons that have data-cmd="speak" the code will also use the attribute data-cmd-data. This attribute value is read and placed into the $event.data object along with the value from data-cmd.

For this button:

<button data-cmd="sayHello">Say Hello</button>

The object $event.data will be:

{
  "cmd": "sayHello",
  "cmdData": undefined
}

For this button:

<button data-cmd="speak" data-cmd-data="Hi">Say Hi</button>

The object $event.data will be:

{
  "cmd": "speak",
  "cmdData": "Hi"
}

You can also include objects in the data-cmd-data attribute.

For this button:

<button data-cmd="buy" data-cmd-data='{"title":"Test Product", "price": 3.95}'>Buy Now</button>

The object $event.data will be:

{
  "cmd": "buy",
  "cmdData": {
    "title": "Test Product",
    "price": 3.95
  }
}

Example: Menus and Toolbars

I had a project that had both a menu, with sub-menus, and a tool bar. Most of the menu items were replicated by the tool bar elements. So the user could perform the same operation using the menu or using a toolbar button.

Here is the toolbar:


And the menu:


And the sub-menu:


The menu was created using one directive and the toolbar was created using a second directive. But both directives just added the attribute data-cmd to the DOM elements and did not process the click events. (With the exception of the menu that would toggle the sub-menu open and closed.)

The menu and toolbar were contained within a single <div> and it was on this <div> that I added the directive iv-on-cmd like below:

<div data-iv-on-cmd="processCmd($event)">
  <ul class="menu">
    <li>
      <button data-ng-click="toggle('batch')">Batch</button>
      <ul class="sub-menu" data-menu="batch">
        <li>...</li>
        ...
      </ul>
    </li>
    <li>
      <button data-ng-click="toggle('image')">Image</button>
      <ul class="sub-menu" data-menu="image">
        <li><button data-cmd="ruler">Ruler</button></li>
        <li><button data-cmd="highlights">Highlights</button></li>
      </ul>
    </li>
    ...
  </ul>
  <div class="toolbar">
    <button class="toolbar__button" data-cmd="unto"><img src="img/undo.png"></button>
    <button class="toolbar__button" data-cmd="redo"><img src="img/redo.png"></button>
    <span class="toolbar__separator"></span>
    <button class="toolbar__button" data-cmd="cut"><img src="img/cut.png"></button>
    <button class="toolbar__button" data-cmd="copy"><img src="img/copy.png"></button>
    <button class="toolbar__button" data-cmd="paste"><img src="img/paste.png"></button>
    <span class="toolbar__separator"></span>
    ...
  </div>
</div>

The controller provided a single function processCmd() that would process each of the commands.

angular.module("mine").factory("myService", myService);
function myService() {
  return {
    "undo": undoFn,    
    "redo": redoFn,    
    "cut": cutFn,    
    "copy": copyFn,    
    "paste": pasteFn    
  };

  function undoFn() {
    // Do something
  }

  function redoFn() {
    // Do something
  }

  function cutFn() {
    // Do something
  }

  function copyFn() {
    // Do something
  }

  function pasteFn() {
    // Do something
  }
}

angular.module("mine").controller("myCtrl", myCtrl);
myCtrl.$inject = ["$scope", "myService"];
function myCtrl($scope, myService) {
  $scope.processCmd = function($event) {
    $event.stopPropigation();
    $event.preventDefault();
    var cmd = $event.data.cmd;

    if (myService.hasOwnProperty(cmd)) { // See is the service supports the command
      myService[cmd]($event.data.cmdData); // $event.data.cmd will default to undefined
    }
    else {
      // Display an error or throw an exception
      // The cmd is not supported in the service 
    }
  }
}

This is a very simple example. But it shows that we can generate simple HTML that supplies data-cmd attributes. Then, with a single command handler, we can process those commands. In this example I also moved the work of processing the commands off to a service. Though you may need to perform async operations or get data back from the service call which would change the way the code was written.

The need for jQuery and not jqLite

This directive requires you to load jQuery before loading Angular. It does not work with the jqLite found in Angular because jqLite does not support the delegate mode of the $().on() function.

jQuery version 1.7 or greater is required because those support the delegate mode of the $().on() function.

<script src="jquery_min.js"></script>
<script src="angular_min.js"></script>


Github repo

The directive iv-on-cmd is part of a set of tools I am maintaining at https://github.com/intervalia/angular-tools

Questions...

I hope that I have provided enough examples to help show the value of the iv-on-cmd directive. If you have questions, please ask them.