Toggle button javascript

When designing sites it is often necessary to switch states in some way, usually pseudo-links are used for this: hide or show a tooltip, an input field, another part of the page. It is possible to write JavaScript code and styles for it every time, but over time this leads to code bloat, which we…

When designing sites it is often necessary to switch states in some way, usually pseudo-links are used for this: hide or show a tooltip, an input field, another part of the page.

It is possible to write JavaScript code and styles for it every time, but over time this leads to code bloat, which we encountered at some point.

However, the problem can be solved much more elegantly. The solution below is very simple and does not require any input from the JavaScript programmer, because the codirector can adjust the styles on his own.

Working Principle

The solution was proposed by our developer Pavel Dovbush aka dpp: the jst class (JavaScript toggle) is added to HTML code of the switching element and the jsw class (JavaScript wrapper) is added to the parent element where changes take place.

The proposed script tracks clicks on elements with the jst class and toggles a specific class on the wrapper element with the jsw class. By default this is the jse (JavaScript enable) class, but it can be given a different name in the rel attribute. The other classes in the element are not affected.

The following code is intentionally simplified to show the idea but is easy to extend or translate to the framework used to support Internet Explorer version 8 and below.

// Click Tracking.
document.addEventListener('click', clickEvent, false);

function clickEvent(e) {
   /* If it is a click on .jst, the
      switch function toggle(). */
   if (e.target && /(^|\s)jst(\s|$)/.test(e.target.className)) {
       toggle(e.target);

       // Cancel the default action (such as clicking a link).
       e.preventDefault();
   }
}

function toggle(el) {
   var cls = el.rel || 'jse',
       // Regular expression for class search and replacement.
       rcls = new RegExp('(^|\\s)' + cls + '(?=\\s|$)');
   // Searching for a wrapping element.
   do {
       el = el.parentNode;
       if (!el) return; // If not found, there is nothing to do.
   } while(!/(^|\s)jsw(\s|$)/.test(el.className));

   // Class Switching.
   if (rcls.test(el.className)) {
       el.className = el.className.replace(rcls, '');
   } else {
       el.className += ' ' + cls;
   }
}

The click event handler is hinged directly on the document node, using event bubbling. This method allows tracking clicks anywhere in the document and does not require loading the document entirely (the document is already there when the scripts are executed) and starts working immediately after the JavaScript containing the code is executed. This improves page initialization speed by eliminating slow DOM queries and event handlers, which would be unavoidable in a traditional approach.

In the code above, the rel attribute is specified with a dot, but it works with links only. To retrieve the attribute of any element, one should use the getAttribute() method. From the semantic point of view, it is more correct to use the data-* attributes from HTML5, which is beyond the scope of this article.

For standard toggle options, it is wise to define standard styles as well. In Badoo, classes like jsh, jsb, jseh, jseb are used for this purpose:

.js .jsb,
.js .jse .jseb { display: block }
.js .jsi,
.js .jse .jsei { display: inline }

.jsb, .jsi,
.js .jsh,
.js .jse .jseh { display: none }

The js class is added by the script immediately at runtime, providing a separation of styling depending on whether JavaScript is enabled or disabled:

document.documentElement.className += ' js'

The jsi and jsb classes enable the display of line and block elements respectively when JavaScript is running, while jsh, on the contrary, hides them. The jsei, jseb and jseh classes work the same as their js counterparts when the jse class is present.

These classes allow flexible control over the behavior of the page and graceful degradation when JavaScript is turned off in the browser.

Advanced use

However, a simple toggle is not always enough, sometimes you need to switch between three or more states, or change something in the populated form, depending on the user’s choice from the proposed options (using radio buttons).

It’s not hard to refine the function for such use as well. To do this, the jss class is defined, on clicking on the element with which, on the parent element with the jsw class will overwrite the class with the value from the rel or value attribute. This option should already be used carefully, because any other class on the element than jsw will be overwritten.

function clickEvent(e) {
    if (!e.target) return;

    // If it is a .jst click, the
    // switch function toggle().
    if (/(^|\s)jst(\s|$)/.test(e.target.className)) {
        toggle(e.target);

        // Cancel the default action (clicking on the link).
        e.preventDefault();
        e.stopPropagation();
    }
    // If the .jss is clicked, the toggle() function
    // the corresponding parameter is passed.
    if (/(^|\s)jss(\s|$)/.test(e.target.className)) {
        toggle(e.target, true);

        // In the case of checkboxes and radio buttons
        // it is necessary to let the buttons switch.
        if (e.target.tagName != 'INPUT') {
            e.preventDefault();
            e.stopPropagation();
        }
    }
}

function toggle(el, set) {
    var cls = el.rel || (el.nodeName == 'INPUT' ?
                        'jse_' + el.value : 'jse');

    // Searching for a wrapping element.
    do {
        el = el.parentNode;
        if (!el) return; // If not found, there is nothing to do.
    } while(!/(^|\s)jsw(\s|$)/.test(el.className));

    if (set) {
        // Setting the class (.jss).
        el.className = 'jsw ' + cls;
    } else {
        // Regular expression for class search and replacement.
        var rcls = new RegExp('(^|\\s)' + cls + '(?=\\s|$)');

        // Switching class (.jst).
        if (rcls.test(el.className)) {
            el.className = el.className.replace(rcls, '');
        } else {
            el.className += ' ' + cls;
        }
    }
}

Conclusion

Instead of re-inventing the wheel every time, it is possible to generalize the class of occuring problems and find a universal solution which satisfies them in 80% of cases.

The feature considered in this article allows solving a lot of common tasks on switching elements on the page by a single layout designer without overloading JavaScript-code and without distracting resources of JavaScript programmers. As a result, the amount of CSS-code is also reduced.

However, there are cases where this feature cannot be used, such as auto-focus on the displayed box (although the autofocus attribute in newer browsers solves this problem) or dynamically loading parts of a page with AJAX requests.

Also, if there are a lot of switchable items, e.g. a huge number of different tabs, the CSS code can be unreasonably bloated – so much so that it makes sense to consider other solutions.

The merits of this feature are simplicity and versatility. It can work in all browsers where JavaScript works, is fully capable of “graceful degradation” in case of non-working scripts, and does not require any extra elements or “hacks”, unlike the method using the hidden and CSS3 selector :checked.

Similar Posts

Leave a Reply

Your email address will not be published.