JavaScript forEach loop - trouble with specific element - javascript

I'm trying to loop through some elements with an attribute and based on which element I click, I'd like the is-active class to be added to the specific element.
The top section works, clicking on the specific tab, but the problem is with tabBody.
Right now, it adds the class to ALL elements and I cannot figure out where to put this to reference the correct div.
I just want whichever tab-body is active, to be the same tabs-bg is active, and have all that based on which tab is clicked.
I have this code (I've tried let el = this, says already defined):
const tab = document.querySelectorAll('[data-tabs-tab]');
const tabBody = document.querySelectorAll('[data-tabs-body]');
function changeMe() {
const activeTab = this.hasAttribute('data-tabs-tab');
[...tab].forEach(el => {
if (this !== el) {
el.classList.remove('is-active');
}
});
this.classList.add('is-active');
[...tabBody].forEach(el => {
if (el.hasAttribute('data-tabs-body') == activeTab) {
if (el.classList.contains('tabs-bg')) {
el.classList.add('is-active');
}
} else {
el.classList.remove('is-active');
}
});
}
tab.forEach(el => el.addEventListener('click', changeMe));
<div class="tabs" data-tabs>
<nav>
<ul class="tabs__tabs">
<li data-tabs-tab="one" class="is-active"></li>
<li data-tabs-tab="two"></li>
<li data-tabs-tab="three"></li>
</ul>
</nav>
<div class="tabs__content">
<div data-tabs-body="one" class="tab-body is-active"></div>
<div data-tabs-body="two" class="tab-body"></div>
<div data-tabs-body="three" class="tab-body"></div>
</div>
</div>
<div class="tabs__bg">
<div data-tabs-body="one" class="tabs-bg is-active"></div>
<div data-tabs-body="two" class="tabs-bg"></div>
<div data-tabs-body="three" class="tabs-bg"></div>
</div>

.hasAttribute() just checks to see if the attribute exists on the element. You should be using .getAttribute() instead to get the value of the attribute.
Also, not sure why the if (el.classList.contains('tabs-bg')) block was in there, but I commented it out to make the demo work.
const tab = document.querySelectorAll('[data-tabs-tab]');
const tabBody = document.querySelectorAll('[data-tabs-body]');
function changeMe() {
const activeTab = this.getAttribute('data-tabs-tab'); // Change here
[...tab].forEach(el => {
if (this !== el) {
el.classList.remove('is-active');
}
});
this.classList.add('is-active');
[...tabBody].forEach(el => {
if (el.getAttribute('data-tabs-body') === activeTab) { // and here
//if (el.classList.contains('tabs-bg')) {
el.classList.add('is-active');
//}
} else {
el.classList.remove('is-active');
}
});
}
tab.forEach(el => el.addEventListener('click', changeMe));
.is-active {color: red;}
<div class="tabs" data-tabs>
<nav>
<ul class="tabs__tabs">
<li data-tabs-tab="one" class="is-active">Tab 1</li>
<li data-tabs-tab="two">Tab 2</li>
<li data-tabs-tab="three">Tab 3</li>
</ul>
</nav>
<div class="tabs__content">
<div data-tabs-body="one" class="tab-body is-active">Tab Body Content 1</div>
<div data-tabs-body="two" class="tab-body">Tab Body Content 2</div>
<div data-tabs-body="three" class="tab-body">Tab Body Content 3</div>
</div>
</div>
<div class="tabs__bg">
<div data-tabs-body="one" class="tabs-bg is-active">Tab Body BG 1</div>
<div data-tabs-body="two" class="tabs-bg">Tab Body BG 2</div>
<div data-tabs-body="three" class="tabs-bg">Tab Body BG 3</div>
</div>

Related

How can I loop through a string array and only get the value of the data attribute that is clicked on

I am trying to get from the <ul class="click-to-section"> to the tester class where I want to loop over the children that has data-section and only show it if it matches up with the <ul class="click-to-section">
See the screenshot of the HTML from Google Dev tools - https://ibb.co/Y3g1jmC
my code to show this is below:
$(".click-to-section li").click(function() {
$(this).each(function(){
let testdata = $(this).data('section');
let testdata2 = $(this).closest("main").next().next().children();
$(testdata2).each(function(){
const dataSection = $(this).data("section");
console.log(dataSection);
});
});
});
HTML
<ul class="click-to-section">
<li data-section="videos">Videos</li> **When this is clicked**
<li data-section="lo">Learning objectives</li>
<li data-section="credits">Credit</li>
<li data-section="toolkits">Toolkit</li>
</ul>
<div class="opinions"></div>
<div class="tester"> **Needs to traverse to this div and loop through the children to show the sections one at a time based on the data-section in the ul menu to equal the data-section here **
<div data-section="credits" class="js-tabs" style="display: none;"</div>
<section data-section="videos" class="js-tabs"></section>
<div class="js-tabs" data-section="lo" style="display: none;"></div>
<div data-section="toolkits" class="js-tabs" style="display: none;"></div>
</div>
Updated the code to click on li and show tester elements that matches data attributes of clicked li
Working Code below
$(".click-to-section li").on("click", function() {
var dataSection = $(this).attr("data-section");
$(".tester > *").hide();
$(".tester [data-section=" + dataSection + "]").show();
})
li {
cursor: pointer
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="click-to-section">
<li data-section="videos">Videos</li> **When this is clicked**
<li data-section="lo">Learning objectives</li>
<li data-section="credits">Credit</li>
<li data-section="toolkits">Toolkit</li>
</ul>
<div class="opinions"></div>
<div class="tester">
<div data-section="credits" class="js-tabs">cred </div>
<section data-section="videos" class="js-tabs">video</section>
<div class="js-tabs" data-section="lo">lo</div>
<div data-section="toolkits" class="js-tabs">js tabs</div>
</div>

Making each li of a Ul clickable

I have a simple ul list, as can be seen below:
<div class="container">
<div class="row">
<div class="col">
<h1>My Favorite Meals <span>(1)</span></h1>
<hr>
<ul>
<li class="selected">Spaghetti</li>
<li>Curry & Rice</li>
<li>Burrito</li>
<li>Soup</li>
<li>Something Else</li>
</ul>
</div>
</div>
</div>
What I want to do, is to make this list clickable. So that when you click on one of the Lis, the background color changes, and the selected li will also be assigned a new class.
My JS code isn't working:
var ul = document.getElementById("foo")
var items = ul.getElementsByTagName("li")
for (var i = 0; i < items.length; ++i) {
// do something with items[i], which is a <li> element
var current = items[i]
current.addEventListener("click", onClick)
var onClick = function() {
current.style.backgroundColor = "red"
}
}
HTML:
<div class="container">
<div class="row">
<div class="col">
<h1>My Favorite Meals <span>(1)</span></h1>
<hr>
<ul>
<li class="selected">Spaghetti</li>
<li>Curry & Rice</li>
<li>Burrito</li>
<li>Soup</li>
<li>Something Else</li>
</ul>
</div>
</div>
</div>
CSS:
.backgroundColor {
background-color: lightgray;
}
JS:
var items = document.getElementsByTagName("li")
items.forEach(li => {
li.addEventListener('click', () => {
li.classList.toggle('backgroundColor');
});
});
Hopefully this will help you to solve your problem.
Try something like this:
<div class="container">
<div class="row">
<div class="col">
<h1>My Favorite Meals <span>(1)</span></h1>
<hr>
<ul id="list">
<li class="selected">Spaghetti</li>
<li>Curry & Rice</li>
<li>Burrito</li>
<li>Soup</li>
<li>Something Else</li>
</ul>
</div>
</div>
</div>
<script>
document.getElementById("list").addEventListener("click",function(e) {
if (e.target && e.target.matches("li")) {
e.target.classList.toggle("foo"); // toggle foo class name here
e.target.style.backgroundColor = "red"; // new background color here
}
});
</script>
I assigned your ul element an id of "list" and added a "click" event listener to it. Whenever you click an li element within that list, it will assign the foo class to that clicked element (click again to unassign). Similarly, assign a red background color to that element.
JavaScript - addEventListener on all created li elements
I hope my code solved your problem.
$(document).ready(()=>{
$(".my-list li").each((i)=>{
var myLi = $($(".my-list li")[i]);
myLi.bind("click",()=>{
if(!myLi.hasClass("selected"))
myLi.addClass("selected")
else
myLi.removeClass("selected")
})
})
})
.selected{
background-color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="my-list">
<li class="selected">Spaghetti</li>
<li>Curry & Rice</li>
<li>Burrito</li>
<li>Soup</li>
<li>Something Else</li>
</ul>
See below. Documentation is inside the code.
// Put a nodelist of li's in variable lis
const lis = document.querySelectorAll("ul li");
// Add an event listener to each li
lis.forEach(li => {
li.addEventListener("click", () => {
// Remove class selected from currently selected li
document.querySelector("ul li.selected").classList.remove("selected");
// Assign class selected to the clicked li
li.classList.add("selected");
});
});
li {
cursor: pointer; /* Change cursor */
}
li.selected {
/* Change background color of selected li */
background-color: lightgreen;
}
<div class="container">
<div class="row">
<div class="col">
<h1>My Favorite Meals <span>(1)</span></h1>
<hr>
<ul>
<li class="selected">Spaghetti</li>
<li>Curry & Rice</li>
<li>Burrito</li>
<li>Soup</li>
<li>Something Else</li>
</ul>
</div>
</div>
</div>

toggle menu with one menu open at a time in pure javascript

I want to make menu toggle and one item from menu is open at a time. I am able to open one item at a time but unable to do toggle at the same time.
var navclick = document.getElementsByClassName("js-dropdown");
var navContent = document.getElementsByClassName('-arrow-link-content');
for (var i = 0; i < navclick.length; i++) {
navclick[i].onclick = function () {
if (this.parentNode.querySelector('div.-arrow-link-content').classList.contains('nav-active')) {
this.parentNode.querySelector('div.-arrow-link-content').classList.remove('nav-active');
}
else {
this.parentNode.querySelector('div.-arrow-link-content').classList.add('nav-active');
}
}
}
.-arrow-link-content {
display:none;
}
.nav-active{ display:block;}
<ul class="c-icons" id="c-iconslist">
<li>
<div class="c-icons__text js-dropdown">heading 1</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 1</div>
</li>
<li>
<div class="c-icons__text js-dropdown">heading 2</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 2</div>
</li>
</ul>
if anyone have any suggestions.. please share.. Thanks in advance
For toggle to work, we need to get all the node with class name -arrow-link-content, and then remove the nav-active class first, then add nav-active to the selected item.
Check the snippet.
var navclick = document.getElementsByClassName("js-dropdown");
var navContent = document.getElementsByClassName('-arrow-link-content');
for (var i = 0; i < navclick.length; i++) {
navclick[i].onclick = function() {
if (this.parentNode.querySelector('div.-arrow-link-content').classList.contains('nav-active')) {
this.parentNode.querySelector('div.-arrow-link-content').classList.remove('nav-active');
return false
}
var el = document.querySelectorAll('div.-arrow-link-content');
for (var i = 0; i < el.length; i++) {
if (el[i].classList.contains('nav-active')) el[i].classList.remove('nav-active');
}
this.parentNode.querySelector('div.-arrow-link-content').classList.add('nav-active');
}
}
.-arrow-link-content {
display: none;
}
.nav-active {
display: block;
}
<ul class="c-icons" id="c-iconslist">
<li>
<div class="c-icons__text js-dropdown">heading 1</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 1</div>
</li>
<li>
<div class="c-icons__text js-dropdown">heading 2</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 2</div>
</li>
</ul>
Since at most one element can be open at a time, we just have to look for that one elment and close it first if it exists, before we open an unopened element
var navclick = document.getElementsByClassName("js-dropdown");
var navContent = document.getElementsByClassName('-arrow-link-content');
for (var i = 0; i < navclick.length; i++) {
navclick[i].onclick = function () {
if (this.parentNode.querySelector('div.-arrow-link-content').classList.contains('nav-active')) {
this.parentNode.querySelector('div.-arrow-link-content').classList.remove('nav-active');
}
else {
try {
// if an open element exists, close it first
this.parentNode.parentNode.querySelector('.nav-active').classList.remove('nav-active');
}
catch (error){
// Error occurs when no open elment exists, in that case: Do nothing
}
this.parentNode.querySelector('div.-arrow-link-content').classList.add('nav-active');
}
}
}
.-arrow-link-content {
display:none;
}
.nav-active{ display:block;}
<ul class="c-icons" id="c-iconslist">
<li>
<div class="c-icons__text js-dropdown">heading 1</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 1</div>
</li>
<li>
<div class="c-icons__text js-dropdown">heading 2</div>
<div class="c-icons__textdropdown -arrow-link-content"> Content 2</div>
</li>
</ul>

Event target - targeting all divs, with a classname in the nextSibling?

New to javascript...
I'm trying to build an event handler, that will set the tabIndex of a set of links to 0 when it's clicked... I'm unsure how to target that exactly.
Example:
<div id="a">Link</div>
<div id="b">
<div class="link" tabIndex="-1">Link 1</div>
<div class="link" tabIndex="-1">Link 2</div>
<div class="link" tabIndex="-1">Link 3</div>
</div>
Im looking ion how to target all the divs with class "link" in #b when I click on #a.
Any advice appreciated!!
In addition to the already given answer I do recommend making use of a more meaningful markup and, if available/possible, taking advantage of a modern DOM.
function nullifyTabIndex(elmNode) {
elmNode.setAttribute('tabindex', 0);
}
function handleNullifyTabIndices(evt) {
var
tabItemList = document.body.querySelectorAll('#b > li');
tabItemList.forEach(nullifyTabIndex);
}
function registerHandlingOfTabindexChange(evt) {
var
elmTrigger = document.body.querySelector('#a');
elmTrigger.addEventListener('click', handleNullifyTabIndices, false);
}
document.addEventListener('DOMContentLoaded', registerHandlingOfTabindexChange, false);
Trigger Tab Index Change
<nav>
<ul id="b">
<li tabindex="-1">Link 1</li>
<li tabindex="-1">Link 2</li>
<li tabindex="-1">Link 3</li>
</ul>
</nav>
The below should help you.
Html:
<div id="a">Link</div>
<div id="b">
<div class="link" tabIndex="-1">Link 1</div>
<div class="link" tabIndex="-1">Link 2</div>
<div class="link" tabIndex="-1">Link 3</div>
</div>
JS:
<script>
window.onload = function () { test() }; //Call the test function below on load
function test() {
document.getElementById("a").addEventListener("click", updateAttribute); //attach event listener to div with id "a"
}
function updateAttribute()
{
var ele = document.getElementsByClassName("link"); //Get all div elements with class name "link" under div "b"
for(var i = 0; i < ele.length; i++) //Loop through all div elements and set attribute "tabIndex" to "0"
ele[i].setAttribute("tabIndex", "0");
}
</script>

showing a particular div dynamically on a html page

I want to show only a particular div by calling a function though onclick event .At a time I just want to show a single div and rest all divs should not show in my web page. I have tried this through using display css property.I just want a single function which can handle this .I can do this question by making more than one function.
<html>
<head>
</head>
<body>
<ul>
<li>1</li><!-- show div1-->
<li>2</li><!-- show div2-->
<li>3</li><!-- show div3-->
</ul>
<div id="first">content 1</div>
<div id="second">content2</div>
<div id="third">content3</div>
</body>
</html>
How about this:
Each div needs the same class so you can find and hide them all at once.
The function needs to know the id of the div to show, so pass in the id.
See changes below:
<html>
<head>
</head>
<body>
<ul id="controls">
<li>1</li><!-- show div1-->
<li>2</li><!-- show div2-->
<li>3</li><!-- show div3-->
</ul>
<div id="first" class="content">content 1</div>
<div id="second" class="content">content2</div>
<div id="third" class="content">content3</div>
<script>
$("#controls a").click(function() {
someFunction($(this).attr("data-target"));
});
function someFunction(divId) {
$(".content").hide();
$("#" + divId).show();
}
</script>
</body>
</html>
Note: You need a reference to jQuery for the $ syntax to work.
FYI, you could do this with just CSS:
.panel {display: none}
.panel:target {display: block}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div class='panel' id="first">content 1</div>
<div class='panel' id="second">content2</div>
<div class='panel' id="third">content3</div>
I think this is what you want:
<ul>
<li id="li_1">1
</li>
<li id="li_2">2
</li>
<li id="li_3">3
</li>
</ul>
<div class='panel' id="li_1_panel">content 1</div>
<div class='panel' id="li_2_panel">content2</div>
<div class='panel' id="li_3_panel">content3</div>
.panel {
display: none;
}
var panelID = "";
for (i = 1; i <= 3; i++) {
alert('#li_' + i);
$('#li_' + i).on('click', function () {
panelID = "#" + $(this).attr("id") + "_panel";
alert(panelID);
$(panelID).toggle();
});
}
Edit
Also, if you did not want to rely on a rigid naming scheme, you could use a hash.
<ul>
<li id="zeus">1
</li>
<li id="athena">2
</li>
<li id="hades">3
</li>
</ul>
<div class='panel' id="isis">content 1</div>
<div class='panel' id="thor">content 2</div>
<div class='panel' id="aphrodite">content 3</div>
var panelID = "";
var li_to_panel = {
"zeus": "isis",
"athena": "thor",
"hades": "aphrodite"
};
$.each(li_to_panel, function(key, value){
alert(key);
liID = "#" + key;
$(liID).on('click', function () {
panelID = "#" + li_to_panel[$(this).attr('id')];
alert(panelID);
$(panelID).toggle();
});
});

Categories