📜 ⬆️ ⬇️

Simple universal javascript switch

When developing websites, it is often necessary to switch their status in some way; usually, pseudo-references are used for this: hide or show a hint, an input field, another part of the page.

You can write JavaScript code and styles to it every time, but over time this leads to a proliferation of code, which we were faced with at some point.

However, the problem can be solved much more elegantly. The solution considered below is simple and does not require the subsequent participation of a JavaScript programmer, since the layout designer will be able to independently make the necessary changes to the styles.

Principle of operation


The solution was proposed by our developer Pavel Dovbush aka dpp : in the HTML code, the jst class ( JavaScript toggle ) is assigned to the switching element, and the parent element inside which changes occur - jsw ( JavaScript wrapper ).
')
The proposed script tracks clicks on elements with the jst class and switches a specific class on the wrapping element with the jsw class. By default, this is the jse class ( JavaScript enable ), but a different name may be specified for it in the rel attribute. Other classes on the element are not affected.

The following is a code that is intentionally simplified to demonstrate an idea, but it is not difficult to supplement it or translate it into a framework used to support Internet Explorer version 8 and below.

//  . document.addEventListener('click', clickEvent, false); function clickEvent(e) { /*     .jst,    toggle(). */ if (e.target && /(^|\s)jst(\s|$)/.test(e.target.className)) { toggle(e.target); //     (,   ). e.preventDefault(); } } function toggle(el) { var cls = el.rel || 'jse', //       . rcls = new RegExp('(^|\\s)' + cls + '(?=\\s|$)'); //   . do { el = el.parentNode; if (!el) return; //    -  . } while(!/(^|\s)jsw(\s|$)/.test(el.className)); //  . if (rcls.test(el.className)) { el.className = el.className.replace(rcls, ''); } else { el.className += ' ' + cls; } } 

The click event handler is “hung” immediately on the document node using “ event bubbling ”. This technique allows you to track clicks anywhere in the document, does not require its full download (the document already exists when executing scripts) and starts working immediately after the JavaScript containing the code has been executed. At the same time, the page initialization speed increases due to getting rid of a series of slow DOM requests to “hang up” event handlers, which would be inevitable under the traditional approach.

The rel attribute in the above code is specified directly through a dot, but this option works only with links. To retrieve an attribute of any element, use the getAttribute () method. From the point of view of semantics, it would be more correct to use the data- * attributes from HTML5, which are beyond the scope of this article.

For standard switching options, it is reasonable to define standard styles. In Badoo, for this, classes like jsh, jsb, jseh, jseb are used :

 .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 when executed, ensuring the separation of the style depending on whether JavaScript is enabled or disabled:

 document.documentElement.className += ' js' 

The jsi and jsb classes , with JavaScript running, display a string and block elements, respectively; jsh , on the contrary, hides. The jsei , jseb, and jseh classes work in the same way as the js counterparts when there is a jse class.

Using these classes, you can flexibly control the behavior of the page, while providing “graceful degradation” (eng. Graceful degradation ) when JavaScript is turned off in the browser.

Examples of using

On the Badoo site, it is not necessary to indicate the real name and surname in the basic information about yourself. For this reason, the designer decided to hide these fields under the pseudo reference:


After clicking on the text, the fields appear, and the pseudo reference itself disappears (the width of the form in the image is reduced).


This can be achieved, for example, using the following HTML code:

 <div class="jsw"> <span class="change jst jsi jseh">    </span> <div class="jsh jseb"> <label for="firstname"></label> <input type="text" id="firstname"> </div> <div class="jsh jseb"> <label for="lastname"></label> <input type="text" id="lastname"> </div> </div> 

The change class on the pseudo-reference sets the appearance, jsi shows it when JavaScript is enabled, and jseh hides it after switching. With the jsh class, the fields are hidden when JavaScript is enabled, and jseb makes them visible after switching.

This ensures that the hiding pseudo reference works. However, when scripts are disabled in the browser, optional fields will be shown, allowing the entire form to be available.

As you can see from the example, the switch does not have to be used only for “round-trip” operation. It can also be used in the case of unilateral actions, such as showing optional fields.

Other examples of use on the site:


Extended use


However, it is not always enough to simply switch, it is sometimes necessary to switch between three or more states, or to change something in the form to be filled, depending on the user's choice from the options offered (using radio buttons).

The function is easy to modify and for such use. To do this, the jss class is defined , by clicking on the element with which, the parent element with the jsw class will overwrite the class with the value from the rel attribute or value . This option should already be used carefully, as any other class on the element, except jsw , will be erased.

 function clickEvent(e) { if (!e.target) return; //     .jst,  //   toggle(). if (/(^|\s)jst(\s|$)/.test(e.target.className)) { toggle(e.target); //     (  ). e.preventDefault(); e.stopPropagation(); } //    .jss,   toggle() //   . if (/(^|\s)jss(\s|$)/.test(e.target.className)) { toggle(e.target, true); //      //    . if (e.target.tagName != 'INPUT') { e.preventDefault(); e.stopPropagation(); } } } function toggle(el, set) { var cls = el.rel || (el.nodeName == 'INPUT' ? 'jse_' + el.value : 'jse'); //   . do { el = el.parentNode; if (!el) return; //    -  . } while(!/(^|\s)jsw(\s|$)/.test(el.className)); if (set) { //   (.jss). el.className = 'jsw ' + cls; } else { //       . var rcls = new RegExp('(^|\\s)' + cls + '(?=\\s|$)'); //   (.jst). if (rcls.test(el.className)) { el.className = el.className.replace(rcls, ''); } else { el.className += ' ' + cls; } } } 

Usage example

Badoo users can make gifts to each other, and there are several overlapping sets to help in choosing it. For example, a cherry is present in all sets, and a soccer ball is only friendly and full.



Somewhat simplistic, the HTML code looks like this:

 <div id="gifts_wrapper" class="jsw all"> <p class="description"> <a href="#" class="jss" rel="all"> </a> <a href="#" class="jss" rel="popular"></a> <a href="#" class="jss" rel="romantic"></a> <a href="#" class="jss" rel="friendship"></a> </p> <div class="gifts_items"> <div class="giftframe romantic popular"> <img src="…" title=""> </div> <div class="giftframe friendship popular"> <img src="…" title=""> </div> <div class="giftframe friendship"> <img src="…" title=" "> </div> <div class="giftframe romantic friendship"> <img src="…" title=" "> </div> <div class="giftframe romantic"> <img src="…" title=""> </div> <div class="giftframe romantic friendship popular"> <img src="…" title=""> </div> </div> </div> 

Each gift is represented by a <div class = "giftframe"> element with a picture of the gift inside, and the additional classes at the element determine the membership of a particular group.

With our function, to control the switching of these sets, only the following styles are needed:

 .gift_items .giftframe { display:none; } .all .gift_items .giftframe, .popular .gift_items .popular, .romantic .gift_items .romantic, .friendship .gift_items .friendship { display:inline-block; } 

An example of using the function with radio buttons can serve as a ticket booking form (unfortunately, we didn’t have an equally obvious example). Depending on the booking of one-way or round-trip tickets, the return date field is displayed or is missing.



HTML code example:

 <div class="jsw"> <label> <input type="radio" name="direction" class="jss" value="oneway">    </label> <label> <input type="radio" name="direction" class="jss" value="twoway"> - </label> <label> : <input type="date" name="fly_date"> </label> <label class="back_date"> : <input type="date" name="back_date"> </label> </div> 

With this code, hiding the return date field can be provided with only one line in CSS:

 .jse_oneway .back_date { display:none } 

Conclusion


Instead of reinventing the wheel every time, you can generalize the class of emerging problems and find a universal solution that satisfies them in 80% of cases.

The function discussed in this article allows you to solve many typical tasks for switching any elements on the page by the coder alone, without overloading the JavaScript code and without diverting the resources of the JavaScript programmers. The amount of CSS as a result is also reduced.

Nevertheless, there are cases when using this function is impossible: for example, automatic focus on the displayed field (although the autofocus attribute in new browsers solves this problem) or dynamic loading of parts of the page through AJAX requests.

Also, with a large number of switchable items, for example, with a huge number of different tabs, the CSS code may be unnecessarily inflated - so much so that it makes sense to consider other solutions.

The advantages of this function are simplicity and versatility. It can work in all browsers where JavaScript is running, has all the features for “elegant degradation” in the case of broken scripts and does not require any extra elements and hacks, unlike the method using the hidden <input type = "radio"> and CSS3 selector : checked .

Update: At the request of workers, several examples are posted here .

Leo Solntsev Aingis , badoo web developer

Source: https://habr.com/ru/post/143714/


All Articles