I have a few click handler events on my page that simply add an active class to an element. I only want the event to trigger if the current section has the class active. I have this working ok but what I am doing is running the same for loop and if statement check for the active class on my section for the different click events. Looking for the simplest way to re-use it using just javascript not jquery.
<section class="section section-1 active">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
<section class="section section-2">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
<section class="section section-3">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
The button click event checks for the active section and just finds the first div tag (hidden-background) and applies an active class to it.
var section = document.querySelectorAll('.section');
var button = document.querySelectorAll('.button');
for (var s = 0; s < section.length; s++) {
if (section[s].classList.contains('active')) {
for (var b = 0; b < button.length; b++) {
button[b].addEventListener('click', function(){
this.getElementsByTagName('div')[0].classList.add('active');
});
}
}
}
The second click event again checks for the active section class and then adds the class active to the hidden-content div.
var section = document.querySelectorAll('.section');
var hiddenContent = document.querySelectorAll('.hidden-content');
for (var s = 0; s < section.length; s++) {
if (section[s].classList.contains('active')) {
for (var h = 0; h < hiddenContent.length; h++) {
hiddenContent[h].addEventListener('click', function(){
this.classList.add('active');
});
}
}
}
Thank you
I would do something like this:
const makeActive = function(evt) {
evt.target.parentElement
.querySelectorAll('.hidden-background, .hidden-content')
.forEach( el => el.classList.add('active'));
};
document
.querySelectorAll('.section.active .button')
.forEach( btn => btn.addEventListener('click', makeActive) );
And assuming the active sections also are toggled by javascript. (The above would no longer work if someone changed the active section, since the eventListener is still bound to the old section)
const makeActive = function(evt) {
if(
evt.target.className.contains('button') &&
evt.target.parentElement.className.contains('active')
){
evt.target.parentElement
.querySelectorAll('.hidden-background, .hidden-content')
.forEach( el => el.classList.add('active'));
}
};
document
.querySelectorAll('.section')
.forEach( section => section.addEventListener('click', makeActive) );
Of course, this whole thing would be easier with only css:
.hidden-background, .hidden-content { display: none; }
.active .hidden-background, .active .hidden-content { display: block; }
but I suppose that is a different question altogether...
Related
I am trying to create tabs with vanilla JavaScript, each tab has an icon and a title inside the link
<div class="tabs-bar-item">
<a href="#tab3">
<img class="icon" src="img/school.png">
<h3 class="title">Instruktāžas datu aizsardzībā</h3>
</a>
</div>
It should open the tab content
<div id="tab3" class="tabs-content-item">
The problem is that .getAttribute("href"); returns null when I have <img> and <h3> elements inside the link.
Everything works if I change the tab to
<div class="tabs-bar-item">
<img class="icon" src="img/school.png">
Instruktāžas datu aizsardzībā
</div>
but I want the tab content to open when clicked anywhere on .tabs-bar-item. How can it be done in vanilla JavaScript?
Full JS code:
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i++) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i++) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target;
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i++) {
tabs[i].addEventListener("click", tabClicks)
}
Css for tab content:
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}
Element.closest searchs up the DOM to find an element matching the selector passed as argument. If Element already matches the selector, Element (the node on which closest is called) is returned.
Hence try changing
let anchorReference = tabClickEvent.target;
to
let anchorReference = tabClickEvent.target.closest('a');
as for example in
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i++) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i++) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target.closest('a');
console.log( anchorReference);
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i++) {
tabs[i].addEventListener("click", tabClicks)
}
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}
<div class="tabs-bar">
<div class="tabs-bar-item">
<a href="#tab3">
<img class="icon" src="img/school.png">
<h3 class="title">Instruktāžas datu aizsardzībā</h3>
</a>
</div>
</div>
<div class=tabs-content>
<div id="tab3" class="tabs-content-item">
content of tab3 with self contained link: <a id="here" href="#here">here</a>. Clicking "here" should not close the tab!
</div>
</div>
First off : you can do this in pure CSS with the :target pseudo-class, no JS needed.
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item:target {
display: block;
}
In the near future, you can also style the tab link for the currently open tab by using the :local-link pseudo-class.
In your code, tabClickEvent.currentTarget always refers to the DIV element that you attached the listener to. That element has no href. There are multiple ways to fix that. Removing the DIV altogether would be a good start, then you can attach a listener to the links, or make a global listener that checks if it's been clicked on a link or its descendant (using element.closest('a')).
Either way, properly implementing navigation with JavaScript is not as easy as you might think (this is also why the pure CSS solution is good). For example, if the user wants to open the link in a new (browser) tab, you'd have to add some code to read the page's fragment on load and open the correct tab without user interaction.
I want an item to show when a link is clicked. I have made an array of all of the links.
I want to add the class "show" to an array of <div>s. Potentially if the solution makes more sense the <div>'s could be within the anchor, it doesn't matter they will overlay the screen.
HTML
<div class="grid-container">
<a href="#"> <!-- lot's of <a>'s will make the array -->
<div class="grid-item">
<div class="grid-item--inner-container">
<div class="grid-item--normal-content">
<div class="grid-item--heading">
<h2>Lending Creativity</h2>
</div>
<div class="grid-item--border"> </div>
</div><!-- end normal content -->
</div><!-- Grid-item-inner -->
</div><!-- Grid item -->
</a>
~
Second part of the HTML.
<div class="popup-container hidden"><!-- many -->
<div class="grid-item--popup-content" id="lending-creativity--popup">
<button class="close-button"><i class="fa fa-times"></i><span class="close-button--text-hidden">Close</span></button>
</div>
</div>
~
JavaScript
let griditems = document.getElementsByClassName('grid-container')[0].getElementsByTagName('a');
let popupitems = document.getElementsByClassName('popup-container');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].style.display = "flex";
})
}
for (let i = 0; i < griditems.length; i++) {
closebuttons[i].addEventListener("click", function () {
closebuttons[i].style.display = "none";
})
}
})
I'm getting an error saying closebuttons is not defined. I guess I'm not 100% sure I'm doing this the right way either, is this the best way to solve this problem?
The issue was closebuttons[i] is undefined. There were more griditems than close buttons.
I could also simplify my JavasScript as the loops should have been the same length.
let griditems = document.getElementsByClassName('grid-container')[0].getElementsByTagName('a');
let popupitems = document.getElementsByClassName('popup-container');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].style.display = "flex";
})
closebuttons[i].addEventListener("click", function () {
closebuttons[i].style.display = "none";
})
}
})
In the end I decided that instead of changing the style I could just add a class or remove it which would then keep my CSS in a CSS/SCSS file. I found this more maintainable and non-tech staff members would could write CSS could easily change this if it was ever needed in the future. It was also easier to maintain if we ever decided to use block instead of flex or something similar. We could just edit it easily in the inspect element to see the outcomes.
Again my code could probably be simplified by not doing 2 loops. But just in case they were different lengths I kept them in their own loops.
Here's my final code.
let griditems = document.getElementsByClassName('grid-item--link');
let popupitems = document.getElementsByClassName('popup-container ');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].classList.remove("hidden");
popupitems[i].classList.add("shown");
})
}
for (let i = 0; i < closebuttons.length; i++) {
closebuttons[i].addEventListener("click", function () {
popupitems[i].classList.add("hidden");
popupitems[i].classList.remove("shown");
})
}
})
I am trying to create a function that finds the id of an element that an anchor tag directs to, and attach a class to that element. I need this to be vanilla js not jquery.
For Example:
<div id="firstDiv"></div>
<div> Destination </div>
<div> Destination #2 </div>
The idea would be that once you click on the anchor tag that it would take you to the div and attach a class to that div to initiate a function.
The script that I've written runs for loops that add the values of the href attribute and the id attribute from javascript objects. See Below:
var rooms = [
{
id: 1,
name: Example,
},
{
id:2,
name: Example #2,
}
]
for ( x in rooms ) {
var i = document.getElementById("firstDiv");
var a = document.createElement("a");
i.appendChild(a);
a.href = "#" + rooms[x].id;
a.innerHTML += rooms[x].name + "<br>";
a.classList.add("show");
}
var rect = document.getElementsByTagName("rect");
for ( i = 0; i < rect.length; i++ ) {
rect[i].setAttribute("id", rooms[i].id);
}
Output:
<div id="firstDiv">
Example
Example #2
</div>
<div id="1"> Destination </div>
<div id="2"> Destination #2 </div>
The function below is an example of what I want the function to do for each corresponding div when an a tag is clicked.
function attach() {
var div = document.getElementById(rooms[i].id);
div.classList.toggle("active");
}
Any advice on how to properly write this function for my particular needs. Would a for loop be best for this or an if/else statement. I've tried both but neither has worked.
Please let me know if I need to clarify more.
It seems like this is what you are asking about. See comments inline.
document.addEventListener("click", function(event){
// First, check to see if it was an anchor that was clicked
// in the document
if(event.target.nodeName === "A"){
// If so, add a class to the target
// Get the href attribute and strip off the "#", then find that element and toggle the class
document.getElementById(event.target.getAttribute("href").replace("#","")).classList.toggle("highlight");
}
});
.highlight { background-color:yellow; }
<div id="firstDiv">
Example
Example #2
</div>
<div id="one"> Destination </div>
<div id="two"> Destination #2 </div>
Try this:
let anchors = document.querySelectorAll("#firstDiv a");
for (let i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("click", () => {
var div = document.getElementById(rooms[i].id);
div.classList.toggle("active");
}
You're almost there you just need to add an onclick property for the anchor tag now you can or cannot use the third attach function.I am including both implementations for your reference.
for ( x in rooms ) {
var i = document.getElementById("firstDiv");
var a = document.createElement("a");
i.appendChild(a);
a.href = "#" + rooms[x].id;
a.innerHTML += rooms[x].name + "<br>";
a.classList.add("show");
a.onClick=`document.getElementById(${rooms[x].id}).classList.toggle("active")`;
}
If you want to have a dedicated function your loop becomes
for ( x in rooms ) {
var i = document.getElementById("firstDiv");
var a = document.createElement("a");
i.appendChild(a);
a.href = "#" + rooms[x].id;
a.innerHTML += rooms[x].name + "<br>";
a.classList.add("show");
a.onClick=`attach(${rooms[x].id})`;
}
Your attach function then becomes:
function attach(id) {
var div = document.getElementById(id);
div.classList.toggle("active");
}
Do let me know if you have any issues.
CSS Only 😎
This is a bit off-topic but you can use the :target selector to set the style of the corresponding id without JS.
/* element with an id matching the URL fragment */
:target {
background: gold;
}
/* you can combine target with other selectors */
#c:target {
background: orange
}
/* just a bit of styling */
a,div {
padding: .5rem;
display: block;
width: 20%;
float:left;
}
Link to A
Link to B
Link to C
Link to D
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
<div id="d">D</div>
I am trying to get the index value of a particular element that is clicked on from a for loop array. What i would like to do is then use this index value and pass it to a variable called indexValue which will then determine a specific div to show. The div it will show will be the one with the same index value that was passed to the variable. I hope this makes sense, I am just not to sure how to achieve this?
The markup would be something like this
HTML
<div class="button"></div>
<div class="button"></div>
<div class="button"></div>
<div class="button"></div>
CSS
.test {
display: none;
}
.show {
display: block;
}
JS
var button = document.querySelectorAll('.button');
for (var i = 0; i < button.length; i++) {
button[i].addEventListener('click', function () {
var indexValue: //Get index value from clicked element
var test = document.querySelectorAll('.test');
test[indexValue].classlist.add('show');
}
}
You can put your eventListener in a closure that takes a parameter from your loop. The alert is only so that when you click it in the fiddle you can see the result of the index
var button = document.querySelectorAll('.button');
for (var i = 0; i < button.length; i++) {
button[i].addEventListener('click', ((j) => {
return function() {
alert(j)
}
})(i))
}
http://jsfiddle.net/cbpw61ur/20/
I have 2 tabs at the top of a page. When one tab is clicked, I would like that tab to have an "active" class and the other tab to have an "inactive" class so that the user can see what tab is currently selected. How can I go about doing this with javascript/css?
<div class="tabActive">
Option 1
</div>
<div id="tabInactive">
Option 2
</div>
another non-jQuery solution could be the following that works with more than two div:
function changeClass(elClass) {
var divsLenght = document.getElementsByTagName("div").length;
for (var i = 0; i < divsLenght; i++) {
document.getElementsByTagName("div")[i].className = "tabInactive";
}
elClass.className = "tabActive";
}
Demo: http://jsbin.com/opetec/2
<div class="tabInactive" onclick="this.classname='tabActive'"></div>
if using jquery:
$("div.tabInactive").click(function() {
$("div.tabInactive").removeClass("tabActive");
$(this).addClass("tabActive");
});
here's a solution that doesn't use any jQuery! it does assume there is only 2 tabs thought.
http://jsfiddle.net/nYpV3/
<div id="tab1" onclick="setToActive(this, 'tab2');">
Option 1
</div>
<div id="tab2" onclick="setToActive(this, 'tab1');">
Option 2
</div>
function setToActive(me, otherId){
me.className='active';
document.getElementById(otherId).className='inactive';
}
Give your tabs a class of "tab"...
HTML:
<div class="tab">
...
</div>
<div class="tab">
...
</div>
JS:
function getByClass(_class, elem) {
var i, result = [], elems = document.getElementsByTagName("div"); //get the elements
for (i = 0; i < elems.length; i++) {
if (elems[i].className.indexOf(_class) !== -1) { //if the elements have the class passed in, add it to the result array
result.push(elems[i]);
}
}
return result;
}
var i, tabs = getByClass("tab", "div"); //get all divs with class tab
for (i = 0; i < tabs.length; i++) { //for each tab...
tabs[i].onclick = function() { //wire up it's click event...
//to clear the other tabs...
var j;
for(j=0; j < tabs.length; j++) {
tabs[j].className = tabs[j].className.replace(" active", "");
}
this.className += " active"; //where active is a class predefined in the CSS
};
}
http://jsfiddle.net/thomas4g/pqMq2/12/
Try this using jQuery
<div class="tab active">
Option 1
</div>
<div class="tab">
Option 2
</div>
<script>
$(function(){
$(".tab").live("click", function(){
$(".tab").removeClass("active");
$(this).addClass("active");
});
});
</script>
This is my guess:
$('.tabActive, #tabInactive').click(function() {
$(this).toggleClass('active');
}