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

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>

Related

How to access a <p> inside three layers of <div> using Javascript?

I'm trying to use Javascript so that a paragraph will alternate between two texts when the user presses a button on the webpage. The problem is that the <p> element I'm trying to manipulate lies within a <div> within a <div> within a <div>.
A low-level mockup of my code can be seen below:
<div id="div1">
<div id="div2">
<div id="div3">
<p>text1</p>
</div>
</div>
</div>
All solutions I've found only state what to do if the <p> is within one <div>; solutions which have not worked for me.
Here's my latest attempt:
function translate() {
var x = document.getElementById("div1").getElementById("div2").getElementById("div3").getElementsByTagName('p')[0];
if (x.innerHTML === "text1") {
x.innerHTML = "text2";
} else {
x.innerHTML = "text1";
}
}
How would I get this to work?
Edit: Nothing seems to be working so far. The <div> element all have classes, but that shouldn't affect things, right?
Edit2: My apologies for taking up your time, but I've found the issue was something else entirely. I'd been using a reserved word for my function call this whole time and had somehow never noticed that that was the issue instead. Thanks again to those who answered, and I shall look deeper at my code before posting my next question.
Why not just use
var x = document.getElementById("div3")
If accessing by the Id directly, it does not really matter what the other DIVs are.
If divs are always in that order, you can do it like this:
var x = document.querySelector('#div1 #div2 #div3 p');
You can use document.querySelectorAll to get an array of all p tags on the page.
const pTags = document.querySelectorAll('p')
pTags.forEach((p, i) => {
if(i % 2 === 0) {
p.innerHTML = 'text1'
} else {
p.innerHTML = 'text2'
}
})
Instead of Ids, you can be more specific & try the following:
var x = document.querySelector('div > div > div > p')
This will be a very strict implementation & follows the condition without the ids or classes.
Use querySelector in order to use the CSS selector #div3 p which means "pick up the paragraph element that is inside the element that has a div3 id".
Then, as you're just changing a string of text, change either the textContent of that element, or its innerText (there are subtle differences between them.)
const para = document.querySelector('#div3 p');
const button = document.querySelector('button');
button.addEventListener('click', translate);
function translate() {
if (para.textContent === 'text1') {
para.textContent = 'text2';
} else {
para.textContent = 'text1';
}
}
<div id="div1">
<div id="div2">
<div id="div3">
<p>text1</p>
</div>
</div>
</div>
<button type="button">Translate</button>

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";
}
};

Hide all elements within a class that don't match the ID of a variable

I have a site with tiles much like the Windows 10 Start menu.
I need these tiles to have fullwidth drop downs once clicked, I'll need the dropdown to close if the same tile is clicked again and I need one dropdown to close others if a different tile is clicked.
These tiles will have all sorts of names so I was hoping to create some javascript that would be dictated by the ID of the tile clicked.
Here's what I have so far:
<div id="gaming" class="box one-one blue" onclick="showSection(this);">
<div id="gaming-section" class="section">
<div id="marketing" class="box one-one blue" onclick="showSection(this);">
<div id="marketing-section" class="section">
function showSection(obj) {
var tileName =obj.getAttribute('id');
var sectionName =(tileName+'-section');
document.getElementById(sectionName).style.display = "block";
}
Any help would be much appreciated
exchange document.getElementById(sectionName).style.display = "block"; for this:
const elemStyle = document.getElementById(sectionName);
if (elem.style.display !== 'block') {
elem.style.display = 'block';
else {
const elements = document.getElementsByClassName('section');
elements.forEach(elem => {
elem.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

Toggling div visibility for a graph

I'm creating a flow graph using HTML, CSS and JS.
This is the fiddle:
http://jsfiddle.net/Newtt/HRedA/3/
This is my JS:
var flag = false;
function showdiv(id) {
var div = document.getElementById(id);
div.style.display = flag ? 'none' : 'block';
flag = !flag;
}
It shows the root node of the graph and has to be clicked on to increase the levels.
As you can see there are some issues regarding the number of clicks required to show the divs that are hidden. I would like some help to fix two things:
Number of clicks required
When I open stage two and stage three, clicking on stage one should collapse all the open stages. However, with the current code, that doesnt seems to be working.
By setting the display-flag on the actual element you will avoid toggling a global state:
function showdiv(id) {
var div = document.getElementById(id);
var divFlag = div.expandedFlag == true
div.style.display = divFlag ? 'none' : 'block';
div.expandedFlag = !divFlag;
}
Or even simpler by using the elements display-state to decide if show/hide:
function showdiv(id) {
var div = document.getElementById(id);
div.style.display = (div.style.display == 'block') ? 'none' : 'block';
}
Including Part 2:
For part two. Including a structure containing children nodes for recursive hiding:
function showdiv(id) {
var div = document.getElementById(id);
var hideFlag = (div.style.display == 'block');
div.style.display = (hideFlag ? 'none' : 'block');
if(hideFlag){hideChildren(id)}
}
var children = {
'two' : ['three-one','three-two']
};
function hideChildren(parent) {
if(children[parent] !== undefined){
for(var i = 0; i<children[parent].length; i++){
var id = children[parent][i]
document.getElementById(id).style.display = 'none';
hideChildren(id)
}
}
}
For part 2 I would again use the DOM to your advantage. I agree that if you intend to generate these dynamically it's going to take some rework of the DOM structure and code.
For this example however, I created the following JSFiddle
http://jsfiddle.net/HRedA/15/
<div class="wrapper">
<div class="stage-one">
<div class="box node-one"></div>
<div class="stage-two" id="two" style='display:none'>
<div class="box node-two">
<div class="stage-three-one" id="three-one" style="display:none;">
<div class="box node-four"></div>
<div class="box node-five"></div>
</div>
</div>
<div class="box node-three">
<div class="stage-three-two" id="three-two" style="display:none;">
<div class="box node-six"></div>
</div>
</div>
</div>
</div>
</div>
So the html above is reduced so it's not cluttering the page, but you'll notice that instead of all the elements being at the same level the nodes are structured inside of one another just like the flow diagram is structured. This means that when you hide a parent it will also hide it's children. This has the side-effect/feature of also remembering what you had expanded. For example if you hide the root and then show it again all your previous expansions will remain.
The reason this won't work dynamically is that all the lines and boxes are positioned by hand. I even had to move some around when they became children of other nodes.
Hope this helps.

Categories

Resources