Target Elements without specific className - javascript

I'm new to JS, but I was hoping there might be a selector like document.getElementsByClassName("div:not(div-1)") that selects elements by class name, unless they also have another class name, that I can define.
Basically my project is a list of movies and their genres.
Using an onclick function, if you click on a genre, I'm trying to hide all other movies that don't belong to that genre. Now I'm looking for a way to select the DIVs without having to add a "not-comedy", "not-action", etc… class to every single movie.
So far I got this:
function test() {
var x = document.getElementsByClassName("movie-div");
var i;
for (i=0; i < x.length; i++) {
if (x[i].style.display === "none") {
x[i].style.display = "block";
} else {
x[i].style.display = "none";
}
}
}

Use querySelectorAll with i.e: the :not() selector
const ELS_x = document.querySelectorAll(".x:not(.zzz)");
ELS_x.forEach(EL => {
// do something with EL
EL.classList.toggle("active");
});
.active {background: gold;}
<div class="x">a</div>
<div class="x zzz">b</div>
<div class="x">a</div>
or if you want to do the class check inside the loop use classList.contains()
const ELS_x = document.querySelectorAll(".x");
ELS_x.forEach(EL => {
if (!EL.classList.contains("zzz")) {
// do something with NON .zzz Elements
EL.classList.toggle("active");
}
});
.active {background: gold;}
<div class="x">a</div>
<div class="x zzz">b</div>
<div class="x">a</div>
if you want to filter out some Elements, use .filter()
const ELS_x = document.querySelectorAll(".x");
const ELS_without_zzz = [...ELS_x].filter(EL => !EL.classList.contains("zzz"));
ELS_without_zzz.forEach(EL => {
// do something with NON .zzz Elements
EL.classList.toggle("active")
});
.active {background: gold;}
<div class="x">a</div>
<div class="x zzz">b</div>
<div class="x">a</div>

Related

Simplify code by applying property to multiple variables at once

I want to simplify this code
let a = document.querySelector(".arrow");
b = document.querySelector(".demo-desc1");
c = document.querySelector(".demo-title h3");
a.style.display = "none";
b.style.display = "none";
c.style.display = "none";
so I don't have to write style.display = "none" for every variable but rather to apply the propery for all variables at once.
Thank you.
If there's only one element of each in the DOM, put them all into a selector string, then iterate over the matching elements.
for (const elm of document.querySelectorAll('.arrow, .demo-desc1, .demo-title h3')) {
elm.style.display = 'none';
}
If there are multiple such elements, then you'll need
const selectors = ['.arrow', '.demo-desc1', '.demo-title h3'];
for (const s of selectors) {
document.querySelector(s).style.display = 'none';
}
But, in this sort of situation, an even better approach would be to toggle a class of a parent container, and have CSS rules that hide those elements when the class is on the parent container. I don't know what the rest of your HTML is like, but perhaps something like
<div class="demo-container">
<more HTML here>
</div>
.hide-children .arrow, .hide-children .demo-desc1, .hide-children .demo-title h3 {
display: none;
}
Then all you need is
document.querySelector('.demo-container').classList.add('hide-children');

Changing attributes with click and querySelector not fully functioning

I've written this to change the background of a div that represents hours on a daily planner. The first instance (onclick) works but the others don't. Do I need to give each .container their own id and their own function?
var changeStatus = document.querySelector("#changeStatus");
var container = document.querySelector(".container");
changeStatus.addEventListener("click", function () {
container.setAttribute("class", "filled");
}
querySelector() is designed to return a single element only. If there are multiple elements matching the .container selector then it will only return the first.
In your case you need to use querySelectorAll() to retrieve all relevant elements, then you need to loop through them to update the class.
var changeStatus = document.querySelector("#changeStatus");
var container = document.querySelectorAll(".container");
changeStatus.addEventListener("click", function () {
container.forEach(el => el.setAttribute("class", "filled"));
});
You can add click listener with loops, something like this:
changeStatus.addEventListener("click", function () {
for(var i=0; i<container.length; i++) {
container[0].setAttribute("class", "filled");
}
}
Here's an example with querySelectorAll as #pilchard answered above:
var changeStatus = document.querySelector("#changeStatus");
var containers = document.querySelectorAll(".container");
changeStatus.addEventListener("click", function () {
containers.forEach(function (container) {
container.setAttribute("class", "filled");
});
});
.filled {
color: #f00;
}
<button id="changeStatus">change status</button>
<div class="container">container 1</div>
<div class="container">container 2</div>
<div class="container">container 3</div>

(Amateur issue) Need to create a function to reduce repetative code

Good evening folks!
I wrote a script that makes it possible to switch between "" pages by clicking on tabs (see image for example)
Problem: The code I've written is very repetative and noob-ish
I have tried writing switch and if/else loops to reduce redundancy, but I'm not good enough to make it.
Could someone help me out?
Thank you very much in advance!
//Getting HTML elements and adding eventListener to trigger function on-click
document.getElementById("archiveBtnOne").addEventListener("click", showFirstTask);
document.getElementById("archiveBtnTwo").addEventListener("click", showSecondTask);
document.getElementById("archiveBtnThree").addEventListener("click", showThirdTask);
document.getElementById("archiveBtnFour").addEventListener("click", showFourthTask);
let firstContent = document.getElementById("aOverviewOne");
let secondContent = document.getElementById("aOverviewTwo");
let thirdContent = document.getElementById("aOverviewThree");
let fourthContent = document.getElementById("aOverviewFour");
//Functions to show current object, and hide other stacked objects
function showFirstTask(){
document.getElementById("aOverviewOne").style.display = "block";
document.getElementById("aOverviewTwo").style.display = "none";
document.getElementById("aOverviewThree").style.display = "none";
document.getElementById("aOverviewFour").style.display = "none";
}
function showSecondTask(){
document.getElementById("aOverviewOne").style.display = "none";
document.getElementById("aOverviewTwo").style.display = "block";
document.getElementById("aOverviewThree").style.display = "none";
document.getElementById("aOverviewFour").style.display = "none";
}
function showThirdTask(){
document.getElementById("aOverviewOne").style.display = "none";
document.getElementById("aOverviewTwo").style.display = "none";
document.getElementById("aOverviewThree").style.display = "block";
document.getElementById("aOverviewFour").style.display = "none";
}
function showFourthTask(){
document.getElementById("aOverviewOne").style.display = "none";
document.getElementById("aOverviewTwo").style.display = "none";
document.getElementById("aOverviewThree").style.display = "none";
document.getElementById("aOverviewFour").style.display = "block";
}
archiveBtnOne represents 'jobb' with its matching colored div - and so on..
Each button is connected to a div with a matching background color
https://i.stack.imgur.com/5BRi9.jpg
Instead of working with id use a common class attribute on similar elements. This way you can easily select them as a collection and apply whatever action needed in a forEach loop.
//get all elements
const items = document.querySelectorAll('.clickable');
//apply event listener to all elements
items.forEach(item => item.addEventListener('click', func));
function func(event) {
//reset all elements
items.forEach(item => {
item.style.color = "black";
});
//apply change to clicked element
event.target.style.color = "blue";
}
<div class="clickable">one</div>
<div class="clickable">two</div>
<div class="clickable">three</div>
There are two main problems with your approach.
By using individual IDs for each element, you have to select them by hand one by one, which gets really verbose really quickly. Hiding each one of them individually requires n*n lines of code, which will get monstruously big really fast as the number of elements increase.
Each time you execute document.getElementById, then you ask Javascript to go all over the hole DOM and search for the designated element. This is very CPU intensive. You should do it once and reuse it, by storing it in a constant : const $elem1 = document.getElementById("elem1") and then reuse $elem1.
Better yet, instead of giving each element an individual name, give them all the same class name and work with that.
const $elems = Array.from(document.getElementsByClassName("elem"));
console.log("elems = ", $elems)
const clickHandler = event => {
$elems.forEach($e => $e.style.display = "none");
event.target.style.display = "block";
};
$elems.forEach($elem => $elem.addEventListener("click", clickHandler));
.elem{
cursor: pointer;
}
<div class="elem">Elem 1</div>
<div class="elem">Elem 2</div>
<div class="elem">Elem 3</div>
<div class="elem">Elem 4</div>
<div class="elem">Elem 5</div>
If including jQuery is an option, it gets even simpler :
const $allElems = $(".elem");
$allElems.click(function() {
$allElems.hide();
$(this).show();
})
.elem {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="elem">Elem 1</div>
<div class="elem">Elem 2</div>
<div class="elem">Elem 3</div>
<div class="elem">Elem 4</div>
<div class="elem">Elem 5</div>

Javascript - get class of another element with onclick

I want to individually toggle to different divs using the same function. Each of these divs has a common class and a different id. The function toggle is called using an onclick parameter on two separate <a> elements:
<a class="btn" id="btnOne" onclick="toggler();">Show/hide divOne</a>
<div class="box" id="divOne">
<a class="btn" id="btnTwo" onclick="toggler();">Show/hide divTwo</a>
<div class="box" id="divTwo">
I first tried to get these divs with getElementsByClassName but, as it returns an HTMLCollection, the script can't target each div individually.
So I tried to select the <a> tags ids (btnOne and btnTwo), but couldn't figure out how to retrieve the divs class using these ids (as we're talking about two different elements here).
In the end, I came back to the getElementById method, as I couldn't figure out how to select them based on their class:
function toggler() {
var id = document.getElementById("divId");
if (id.style.display === "none") {
id.style.display = "block";
} else {
id.style.display = "none";
}
};
This leaves me with two functions instead of just one. Any suggestion on how to target the two divs individually?
You can access the next sibling using nextElementSibling presuming the box will always be right after the hyperlink.
// Put the buttons into an array
const buttons = [...document.getElementsByClassName("btn")];
// Assing an event listener for every button
buttons.map(button => button.addEventListener("click", function(e) {
// Find the next sibling
const box = e.target.nextElementSibling;
// Toggle the display value
if (box.style.display === "none") {
box.style.display = "block";
} else {
box.style.display = "none";
}
}));
a {
display: block;
}
.box {
width: 5rem;
height: 2rem;
background-color: blue;
}
<a class="btn">Show/hide divOne</a>
<div class="box"></div>
<a class="btn">Show/hide divTwo</a>
<div class="box"></div>
There is a simple way to select the divs with their class name and you already used it.
The answer is getElementsByClassName. But in vanilla JS things are a little bit (over)complicated.
It will not target both divs individually. Instead, if you want to select the first div with this class you would do it like this:
getElementsByClassName('classname')[0]
If you want to select the second div you would use:
getElementsByClassName('classname')[1]
and so on. But there is a way of course.
You want to use loops:
var x = document.getElementsByClassName("classname");
for (var i = 0; i < x.length; i++) {
if (x[i].style.display === "none") {
x[i].style.display = "block";
} else {
x[i].style.display = "none";
}
}
In this way, you will target ALL divs with this class.
I'd dynamically add the events on the switches, using their classes. I added the class showHideDivBtn to them. To make sure you know which div you have to toggle, I used a data-id.
With addEventListener, I can use the event variable I named e. With this one, I have access to properties, such as the data-id I wrote.
let buttons = document.getElementsByClassName("showHideDivBtn");
for (let i = 0; i < buttons.length; ++i)
{
buttons[i].addEventListener('click', function(e)
{
let divToToggle = document.getElementById(e.srcElement.dataset.id);
if (divToToggle.style.display === "none")
divToToggle.style.display = "block";
else
divToToggle.style.display = "none";
});
}
<a class="btn showHideDivBtn" data-id="divOne" id="btnOne">Show/hide divOne</a>
<div class="box" id="divOne">One</div>
<br />
<a class="btn showHideDivBtn" data-id="divTwo" id="btnTwo">Show/hide divTwo</a>
<div class="box" id="divTwo">Two</div>
Use substr to get the word after extracting 'btn' from anchor id which will result in One or Two then while defining the if use "div"+word this will get the div by it is related a tag
function toggler() {
var word=this.id.substr(3);
var id = document.getElementById("div"+word);
if (id.style.display === "none") {
id.style.display = "block";
} else {
id.style.display = "none";
}
};

changing clicked element's style in array through javascript

my html code:
<div class="mydiv" onclick="clickonelement()">div1</div>
<div class="mydiv" onclick="clickonelement()">div2</div>
<div class="mydiv" onclick="clickonelement()">div3</div>
<div class="mydiv" onclick="clickonelement()">div4</div>
<div class="mydiv" onclick="clickonelement()">div5</div>
<div class="mydiv" onclick="clickonelement()">div6</div>
<!-- javascript code -->
function clickonelement(){
mydiv = document.getElementsByClassName("mydiv");
for(i=0; i<mydiv.length; i++){
mydiv.item(i).style.backgroundColor = "red";
mydiv[this].style.backgroundColor = "#fff";
}
}
css code
.mydiv{width:300px; height:30px;}
I want on onClick event to change clicked element's background to white and other elements background remain red in color but my code
mydiv[this].style.backgroundColor = "#fff";
is not working. please solve this problem in JavaScript only. I am in basic stage of JavaScript.
You need to pass a reference to the element that you want to refer to with this, e.g. onclick="clickonelement(this)":
function clickonelement(elem) {
mydiv = document.getElementsByClassName("mydiv");
for (i = 0; i < mydiv.length; i++) {
mydiv.item(i).style.backgroundColor = "red";
elem.style.backgroundColor = "#fff";
}
}
<div class="mydiv" onclick="clickonelement(this)">div1</div>
<div class="mydiv" onclick="clickonelement(this)">div2</div>
<div class="mydiv" onclick="clickonelement(this)">div3</div>
<div class="mydiv" onclick="clickonelement(this)">div4</div>
<div class="mydiv" onclick="clickonelement(this)">div5</div>
<div class="mydiv" onclick="clickonelement(this)">div6</div>
This is JS code for your HTML code, you need add addEventListener.
function clickonelement() {
mydiv = document.getElementsByClassName("mydiv");
for (var i = 0; i < mydiv.length; i++) {
mydiv[i].addEventListener('click', function() {
this.style.backgroundColor = "red";
this.style.backgroundColor = "#fff";
});
}
}
Here is just another way of achieving the same functionality.
Objective
To remove inline event handler
Use loop only once instead of looping over all the matched class name (mydiv) on every click.
Used javascript functions & concepts
querySelectorAll : - Used to select all matched elements with same class that is mydiv. It will return an nodelist containing all matched elements
forEach:- is an array method which used to loop over list.It accepts three arguments. For this case two will be enough.
addEventListener:- Is use to attach event to an element
Closures:These functions 'remember' the environment in which they were created.
Hope this snippet will be useful
//Get all the matched Elements
var matches = document.querySelectorAll("div.mydiv");
//Use an variable to rememeber previous clicked element
var prevIndex = -1; //
// Loop over the list
matches.forEach(function(item,index){
(function(i){ // A closure is created
item.addEventListener('click',function(){
// if any previous element was clicked then rest it's background
if(prevIndex !== -1){
matches[prevIndex].style.background="red";
}
// change background of current element
item.style.background="#fff";
// update prevIndex
prevIndex = i;
})
}(index))
})
Check this DEMO

Categories

Resources