I've created a 3 way toggle with 3 states disabled
, default
, enabled
.
On clicking each input
, the corresponding div's should be displayed.
var content = function() {
var divs = ["div-data1", "div-data2", "div-data3"];
var visibleDivId = null;
function toggle() {
//code
}
function init() {
toggle();
}
return {
init: init,
}
}();
window.onload = function() {
content.init();
};
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" rel="stylesheet"/>
<div class="tw-toggle">
<input type="radio" name="toggle" class="threeToggle1" value="false">
<label class="toggle toggle-yes"><i class="fa fa-times"></i></label>
<input checked type="radio" name="toggle" class="threeToggle2" value="-1">
<label class="toggle toggle-yes"><i class="fa fa-minus"></i></label>
<input type="radio" name="toggle" class="threeToggle3" value="true">
<label class="toggle toggle-yes"><i class="fa fa-check"></i></label>
</div>
<div class="div-data1" style="display:none"> div1 </div>
<div class="div-data2" style="display:none"> div2</div>
<div class="div-data3" style="display:none">div3 </div>
How could the toggle works, without passing onclick
to HTML ?
Could someone please help.
Thanks.
One approach is as follows, with explanatory comments in the code:
// a named function, using Arrow syntax, to handle the toggling; this passes
// a reference to the Event Object (automagically, from the later use of
// EventTarget.addEventListener()):
const toggle = (event) => {
// we retrieve the current element to which the event-handler was bound:
let current = event.currentTarget,
// we retrieve the parentNode of that element:
parent = current.parentNode,
// we use an Array-literal, with the spread syntax, to create an
// Array of the parent-element's element-children
children = [...parent.children]
// and we filter that Array of elements with the anonymous Arrow
// function of the Array.prototype.filter() method:
.filter(
// here we pass 'el', a reference to the current Element of the
// Array of Elements we're iterating over, to retain only the
// elements which have a tagName exactly equal to the tagName
// of the current element:
(el) => current.tagName === el.tagName
),
// using Array.prototype.findIndex() to retrieve the index of the
// 'current' element from an Array containing it, and its siblings:
currentIndex = children.findIndex(
// here we again pass in a reference to the current element 'el'
// of the Array of elements, and retrieve the element which is
// the 'current' (variable-name) element:
(el) => el === current
);
// we use document.querySelectorAll() to retrieve the elements matching
// the selector stored in the element's 'data-group' attribute:
document.querySelectorAll(current.dataset.group)
// iterating over those elements, with NodeList.prototype.forEach():
.forEach(
// passing in a reference to the current element ('el'), and the
// index of the current element ('index'); here we update the
// opacity property, by assessing whether the 'index' variable
// is exactly-equal to the 'currentIndex' variable. If it is,
// we return 1 (so the element is fully visible), otherwise
// we return an invalid empty-string, which removes the
// opacity from the inline style attribute:
(el, index) => el.style.opacity = index === currentIndex ? 1 : ''
);
},
// creating a custom Event:
changeEvent = new Event('change');
// using document.querySelectorAll() to find all <input> elements inside of
// a .toggleGroup element, and iterating over that NodeList with
// NodeList.prototype.forEach():
document.querySelectorAll('.toggleGroup input').forEach(
// here we - again - pass in a reference to the current element ('el'),
// and use EventTarget.addEventListener() to bind the toggle() function
// (note the deliberate omission of the parentheses) as the event-
// handler for the 'change' event fired on the elements:
(el) => {
el.addEventListener('change', toggle);
// using a Yoda condition to see if the current element is exactly-equal
// to true (that way we can't accidentally use assignment ('=') instead
// of comparison ('==' or '===') without generating an error:
if (true === el.checked) {
// triggering the 'change' event in order to have the correct element
// show on page-load:
el.dispatchEvent(changeEvent);
}
}
);
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
main {
inline-size: clamp(15em, 50%, 900px);
margin-block: 1em;
margin-inline: auto;
}
.toggleGroup,
.groupToToggle {
display: flex;
gap: 1em;
justify-content: center;
margin-block: 1em;
}
.div-data {
border: 1px solid currentColor;
border-radius: 0.25em;
opacity: 0.2;
padding-block: 0.25em;
padding-inline: 0.5em;
transition: opacity 0.4s linear;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" type="text/css">
<main>
<div class="tw-toggle toggleGroup">
<!--
Added an id attribute, in order that we can associate the <input> with the <label>,
I also added a 'data-group' attribute, which contains an attribute-value which
serves as the CSS selector for the relevant group of elements:
-->
<input id="tw-toggle-1" type="radio" name="toggle" class="threeToggle1" value="false" data-group=".div-data">
<!--
As above, the 'for' attribute (the attribute-value of which is identical to the
(unique) 'id' attribute of the relevant element in order to associate the <input>
and <label> together:
-->
<label for="tw-toggle-1" class="toggle toggle-yes"><i class="fa fa-times"></i></label>
<input id="tw-toggle-2" checked type="radio" name="toggle" class="threeToggle2" value="-1" data-group=".div-data">
<label for="tw-toggle-2" class="toggle toggle-yes"><i class="fa fa-minus"></i></label>
<input id="tw-toggle-3" type="radio" name="toggle" class="threeToggle3" value="true" data-group=".div-data">
<label for="tw-toggle-3" class="toggle toggle-yes"><i class="fa fa-check"></i></label>
</div>
<!--
Added a wrapper to group the related elements together:
-->
<div class="groupToToggle">
<!--
Added a 'div-data' class-name to easily target all elements with CSS, and
removed the inline styles preferring instead to move presentation entirely
to the CSS:
-->
<div class="div-data div-data1">div1</div>
<div class="div-data div-data2">div2</div>
<div class="div-data div-data3">div3</div>
</div>
</main>
References: