JQuery Traversing through DOM on click - javascript

I am having problem of traversing through each HTML element one by one.There are two buttons #up and #down.On click of #up the id #myID should move to the next element upwards and vice versa for #down.The problem is I am able to move through the siblings but not through the child elements.
For example if I click on #down the id #myID should have moved to p tag which is the child of that div on next click to span which is child of p then on next click to div.But in my code it is directly jumping to div ignoring the children.
JSFIDDLE
Here is the code:
$("#up").click(function() {
$("#startHere").find("#myID").next().attr('id', 'myID');
$('#startHere').find("#myID").removeAttr('id');
});
$("#down").click(function() {
$("#startHere").find("#myID").prev().attr('id', 'myID');
$('#startHere').find("#myID").next().removeAttr('id');
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="up">GO DOWN</button>
<button id="down">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>

I think you can just find all the elements first, jQuery returns them in DOM order, which is what you want. No need to search for the next/prev element on-the-fly.
var allElements = $("#startHere").find('*');
var currentIndex = allElements.index('#myID');
function move(delta) {
// Find the new index
var index = currentIndex + delta;
// Clamp to 0…lengh of list
// Here we could also make it wrap instead
index = Math.max(Math.min(index, allElements.length - 1), 0);
// Remove the ID from the old element
allElements.eq(currentIndex).removeAttr('id');
// Add the ID to the new element
allElements.eq(index).attr('id', 'myID');
// Update the index
currentIndex = index;
}
$("#up").click(function() {
move(1);
});
$("#down").click(function() {
move(-1);
})
var allElements = $("#startHere").find('*');
var currentIndex = allElements.index('#myID');
function move(delta) {
// Find the new index
var index = currentIndex + delta;
// Clamp to 0…lengh of list
// Here we could also make it wrap instead
index = Math.max(Math.min(index, allElements.length - 1), 0);
// Remove the ID from the old element
allElements.eq(currentIndex).removeAttr('id');
// Add the ID to the new element
allElements.eq(index).attr('id', 'myID');
// Update the index
currentIndex = index;
}
$("#up").click(function() {
move(-1);
});
$("#down").click(function() {
move(1);
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="down">GO DOWN</button>
<button id="up">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>
If you do need the elements on-the-fly (because they might have changed), you can still use the same tactic (and simply build up the allElements list in the move function and get the index using allElements.index('#myID')) but it might be more performant to update the list only when you know it changed (after an Ajax request, after modification on event handlers, etc.).
Edit:
The code for searching the next/prev element on-the-fly is a bit more work because it has to recurse when traversing up but makes it possible to have a different set of rules for up vs. down movement.
var boundary = $("#startHere");
function findNext(node, anchor) {
if(!anchor && node.children(':first-child').length) {
return node.children(':first-child');
}
if(node.next().length) {
return node.next();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findNext(node.parent(), anchor||node);
}
function findPrev(node, anchor) {
if(!anchor && node.children(':last-child').length) {
return node.children(':last-child');
}
if(node.prev().length) {
return node.prev();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findPrev(node.parent(), anchor||node);
}
function move(finder) {
// Find the current item
var current = boundary.find('#myID');
// Find the next item
var next = finder(current);
// Remove the ID from the old element
current.removeAttr('id');
// Add the ID to the new element
next.attr('id', 'myID');
}
$("#up").click(function() {
move(findPrev);
});
$("#down").click(function() {
move(findNext);
})
var boundary = $("#startHere");
function findNext(node, anchor) {
if(!anchor && node.children(':first-child').length) {
return node.children(':first-child');
}
if(node.next().length) {
return node.next();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findNext(node.parent(), anchor||node);
}
function findPrev(node, anchor) {
if(!anchor && node.children(':last-child').length) {
return node.children(':last-child');
}
if(node.prev().length) {
return node.prev();
}
if(!boundary.find(node.parent()).length) {
// Out of boundary. Stick to the last node
return anchor||node;
}
return findPrev(node.parent(), anchor||node);
}
function move(finder) {
// Find the current item
var current = boundary.find('#myID');
// Find the next item
var next = finder(current);
// Remove the ID from the old element
current.removeAttr('id');
// Add the ID to the new element
next.attr('id', 'myID');
}
$("#up").click(function() {
move(findPrev);
});
$("#down").click(function() {
move(findNext);
})
#myID {
border: 2px solid yellow;
}
#startHere {
height: 100%;
width: 50%;
}
div {
height: 100px;
width: 100px;
border: 2px solid;
margin: 10px;
}
p {
height: 50px;
width: 50px;
border: 2px solid blue;
margin: 10px;
}
h1 {
height: 40px;
width: 40px;
border: 2px solid red;
margin: 10px;
}
span {
display: block;
height: 25px;
width: 25px;
border: 2px solid green;
margin: 10px;
}
button {
height: 25px;
width: 100px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="down">GO DOWN</button>
<button id="up">GO UP</button>
<div id="startHere">
<div id="myID">
<p><span></span></p>
</div>
<div><span></span></div>
<div>
<h1></h1>
</div>
<p></p>
<h1></h1>
<p><span></span></p>
</div>
This is really bad UI. To select some nodes in some states, you first have to navigate “UP” and then “DOWN” again. But it seems to do what you ask for.

Related

javascript buttons how do i delete a class from all buttons when a certain value is selected?

I have a couple of buttons with one button that should disable all others. I wrote a code that selects buttons by adding a class and when clicked again deletes the class. It also pushed the value into an array. I want to make the no preference button in my code to delete a certain class from all buttons, except for the no preference button.
I already made it so it deletes everything in the array when it is clicked, but I just gotta delete the class from all buttons.
Code:
let div = document.getElementById('buttonDiv');
let arr = [];
div.addEventListener("click", function (event) {
let tgt = event.target;
function SelectedClass() {
if (tgt.classList.contains('Selected')) {
tgt.classList.remove('Selected');
} else {
tgt.classList.add('Selected');
}
}
if (tgt.classList.contains('buttons')) {
if (arr.indexOf(tgt.value) === -1) {
if (tgt.value === 'Ignore') {
if (tgt.classList.contains('Selected')) {
tgt.classList.remove('Selected');
} else {
tgt.classList.add('Selected');
arr = [];
}
} else {
SelectedClass();
arr.push(tgt.value);
}
} else {
arr.splice(arr.indexOf(tgt.value), 1);
SelectedClass();
}
}
console.log(arr);
})
.buttondiv {
position: relative;
width: 200px;
height: 675px;
margin-left: 50%;
transform: translateX(-50%);
margin-top: 50px;
}
.buttons {
width: 275px;
height: 50px;
display: inline-block;
margin-bottom: 15px;
;
border: 2px solid black;
border-radius: 3px;
background-color: white;
color: black;
}
.Selected {
background-color: orangered;
color: white;
border: none;
}
<div class="buttondiv" id="buttonDiv">
<button value="btn1" class="buttons">1</button>
<button value="btn2" class="buttons">2</button>
<button value="btn3" class="buttons">3</button>
<button value="btn4" class="buttons">4</button>
<button value="Ignore" class="buttons">No Preference</button>
</div>
I tried doing it with a for loop and a queryselector, but that didn't work. Does anybody know a solution?
If I understand you correctly the code can be simplified. See example below where different actions are taken place based on weather you press the no preference button or an other button. For this I added a class to the no preference button so we can easily query on that.
let div = document.getElementById('buttonDiv');
let arr = [];
div.addEventListener("click", function (event) {
let tgt = event.target;
if (tgt.classList.contains('buttons')) {
//when no preference is clicked remove all selected classes and empty the array
if(tgt.value === 'Ignore') {
event.currentTarget.querySelectorAll('.buttons').forEach((el) => {
el.classList.remove('Selected');
arr = [];
});
}
//when other button is clicked removed the selected class from the no preference button and push the current value to the array
else {
event.currentTarget.querySelector('.buttons.ignore').classList.remove('Selected');
arr.push(tgt.value);
}
//always add selected class to the current button.
tgt.classList.add('Selected');
}
console.log(JSON.stringify(arr));
})
.buttondiv {
position: relative;
width: 200px;
height: 675px;
margin-left: 50%;
transform: translateX(-50%);
margin-top: 50px;
}
.buttons {
width: 275px;
height: 50px;
display: inline-block;
margin-bottom: 15px;
;
border: 2px solid black;
border-radius: 3px;
background-color: white;
color: black;
}
.Selected {
background-color: orangered;
color: white;
border: none;
}
<div class="buttondiv" id="buttonDiv">
<button value="btn1" class="buttons">1</button>
<button value="btn2" class="buttons">2</button>
<button value="btn3" class="buttons">3</button>
<button value="btn4" class="buttons">4</button>
<button value="Ignore" class="buttons ignore">No Preference</button>
</div>
As you can see from my example i add a querySelectorAll to all button except for ignore button, when user click to "No Preference" forEach will disabled or enabled all.
let div = document.getElementById('buttonDiv');
let arr = [];
div.addEventListener("click", function(event) {
let tgt = event.target;
function SelectedClass() {
if (tgt.classList.contains('Selected')) {
tgt.classList.remove('Selected');
} else {
tgt.classList.add('Selected');
}
}
if (tgt.classList.contains('buttons')) {
if (arr.indexOf(tgt.value) === -1) {
if (tgt.value === 'Ignore') {
if (tgt.classList.contains('Selected')) {
tgt.classList.remove('Selected');
document.querySelectorAll('button:not(.ignore)').forEach(el => {
el.disabled = false;
});
} else {
tgt.classList.add('Selected');
document.querySelectorAll('button:not(.ignore)').forEach(el => {
if (el.classList.contains('Selected')) {
el.classList.remove('Selected');
}
el.disabled = true;
});
arr = [];
}
} else {
SelectedClass();
arr.push(tgt.value);
}
} else {
arr.splice(arr.indexOf(tgt.value), 1);
SelectedClass();
}
}
console.log(arr);
})
.buttondiv {
position: relative;
width: 200px;
height: 675px;
margin-left: 50%;
transform: translateX(-50%);
margin-top: 50px;
}
.buttons {
width: 275px;
height: 50px;
display: inline-block;
margin-bottom: 15px;
;
border: 2px solid black;
border-radius: 3px;
background-color: white;
color: black;
}
.Selected {
background-color: orangered;
color: white;
border: none;
}
<div class="buttondiv" id="buttonDiv">
<button value="btn1" class="buttons">1</button>
<button value="btn2" class="buttons">2</button>
<button value="btn3" class="buttons">3</button>
<button value="btn4" class="buttons">4</button>
<button value="Ignore" class="buttons ignore">No Preference</button>
</div>
Reference:
Document.querySelectorAll()
disabled

How can I add a class to an element from an array on hover?

I have an array of elements. I want when I hover on any element from this array to add a class to said element. How can I do that?
Loop through each item in the array and an event listener for mouseover which adds the class.
const array = document.querySelectorAll('div');
const classToAdd = 'red';
array.forEach(e => e.addEventListener('mouseover', () => {
e.classList.add(classToAdd);
}))
div {
height: 100px;
width: 100px;
border: 1px solid;
}
.red {
background-color: red;
}
<div></div>
<div></div>
<div></div>
<div></div>
If you have multiple elements with different ids or class you can try this.
let elements = ['#hud-menu', '#hud-intro', '.hud-shop', '.hud-settings'];
document.querySelectorAll(elements).forEach(element => {
element.addEventListener('mouseover', () => {
element.classList.add('blue');
});
});
div {
height: 100px;
width: 100px;
border: 2px solid;
border-radius: 10px;
text-align: center;
}
.blue {
background-color: blue;
}
<div id="hud-menu">Hud Menu</div><br>
<div id="hud-intro">Hud Intro</div><br>
<div class="hud-shop">Hud Shop</div><br>
<div class="hud-settings">Hud Settings</div>
And just in case if you want to remove the class when you stop hovering over an element you can use mouseout like the example below.
let elements = ['#hud-menu', '#hud-intro', '.hud-shop', '.hud-settings'];
// Add class on mouseover
document.querySelectorAll(elements).forEach(element => {
element.addEventListener('mouseover', () => {
element.classList.add('blue');
});
});
// Remove class on mouseout
document.querySelectorAll(elements).forEach(element => {
element.addEventListener('mouseout', () => {
element.classList.remove('blue');
});
});
div {
height: 100px;
width: 100px;
border: 2px solid;
border-radius: 10px;
text-align: center;
}
.blue {
background-color: blue;
}
<div id="hud-menu">Hud Menu</div><br>
<div id="hud-intro">Hud Intro</div><br>
<div class="hud-shop">Hud Shop</div><br>
<div class="hud-settings">Hud Settings</div>

How to get html element without its children in JavaScript?

I would like to get an html element without its children, to set an event addEventListener("click) on it, so that the function will only be executed when it is clicked, not on its children. I can only use Javascript. Is this possible?
const divs = document.querySelectorAll("div");
const body = document.querySelector("body");
const myFunction = function() {
this.classList.add("clicked")
}
divs.forEach(function(element) {
element.addEventListener("click", myFunction)
});
.grandparent {
padding: 20px;
border: 5px solid black;
}
.parent {
padding: 20px;
border: 5px solid blue;
}
.child {
padding: 20px;
height: 100px;
width: 100px;
border: 5px solid yellow;
}
.clicked {
background-color: red;
}
<div data-time="3000" class="grandparent">
<div data-time="2000" class="parent">
<div data-time="1000" class="child"></div>
</div>
</div>
this function adds a class to each div, now I would like clicking outside of div to remove that class, however the body variable contains including its children.
If you want to ignore all and any child clicks, then check if the currentTarget is different than the target of the event.
target is the element the event originated from (the deepest child that received the event)
currentTarget is the element on which the event handler is attached
const divs = document.querySelectorAll("div");
const body = document.querySelector("body");
const myFunction = function(event) {
if (event.target === event.currentTarget) {
this.classList.add("clicked")
}
}
divs.forEach(function(element) {
element.addEventListener("click", myFunction)
});
.grandparent {
padding: 20px;
border: 5px solid black;
}
.parent {
padding: 20px;
border: 5px solid blue;
}
.child {
padding: 20px;
height: 100px;
width: 100px;
border: 5px solid yellow;
}
.clicked {
background-color: red;
}
<div data-time="3000" class="grandparent">
<div data-time="2000" class="parent">
<div data-time="1000" class="child"></div>
</div>
</div>

i want to make multiple mouseover functions with minimum codes

I have 10 links and each of them is different from the others.I want when user hovers on them background image of the div changes and a tooltip text be shown on top of the links with a fade-in animation .
i have tried to make several functions using JS and it works but it's a lot of code and mostly repetitive.I want a good shortcut through all of that useless coding.
document.getElementById("d1").onmouseover = function() {
mouseOver1()
};
document.getElementById("d2").onmouseover = function() {
mouseOver2()
};
document.getElementById("d3").onmouseover = function() {
mouseOver3()
};
document.getElementById("d1").onmouseout = function() {
mouseOut1()
};
document.getElementById("d2").onmouseout = function() {
mouseOut2()
};
document.getElementById("d3").onmouseout = function() {
mouseOut3()
};
function mouseOver1() {
document.getElementById("dogs").style.background = "blue";
document.getElementById("tooltiptext1").style.visibility = "visible";
}
function mouseOut1() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext1").style.visibility = "hidden";
}
function mouseOver2() {
document.getElementById("dogs").style.background = "green";
document.getElementById("tooltiptext2").style.visibility = "visible";
}
function mouseOut2() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext2").style.visibility = "hidden";
}
function mouseOver3() {
document.getElementById("dogs").style.background = "red";
document.getElementById("tooltiptext3").style.visibility = "visible";
}
function mouseOut3() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext3").style.visibility = "hidden";
}
#dogs {
float: right;
margin-top: 5%;
background: black;
width: 150px;
height: 150px;
}
#d-list {
color: white;
direction: ltr;
float: right;
width: 60%;
height: 60%;
}
#tooltiptext1,
#tooltiptext2,
#tooltiptext3 {
color: black;
background-color: gray;
width: 120px;
height: 30px;
border-radius: 6px;
text-align: center;
padding-top: 5px;
visibility: hidden;
}
<div id="animals">
<div id="dogs"></div>
<div id="d-list">
<pre style="font-size:22px; color:darkorange">dogs</pre><br />
<pre>white Husky</pre>
<p id="tooltiptext1">Tooltip text1</p>
<pre>black Bull</pre>
<p id="tooltiptext2">Tooltip text2</p>
<pre>brown Rex</pre>
<p id="tooltiptext3">Tooltip text3</p>
</div>
</div>
Please have in mind that all of links will change same outer div object and the idea is to change the background image of that div and the tooltip shoud appear on the top of the links....so,
any ideas?
edit: added animation requested.
CSS is almost always better done in script by using classes when multiple elements are being manipulated with similar functions so I used that here. Rather than put some complex set of logic in place I simply added data attributes for the colors - now it works for any new elements you wish to add as well.
I did find your markup to be somewhat strangely chosen and would have done it differently but that was not part of the question as stated.
I took the liberty of removing the style attribute from your dogs element and put it in the CSS also as it seemed to belong there and mixing markup and css will probably make it harder to maintain over time and puts all the style in one place.
Since you DID tag this with jQuery here is an example of that.
$(function() {
$('#d-list').on('mouseenter', 'a', function(event) {
$('#dogs').css('backgroundColor', $(this).data('colorin'));
$(this).parent().next('.tooltip').animate({
opacity: 1
});
}).on('mouseleave', 'a', function(event) {
$('#dogs').css('backgroundColor', $(this).data('colorout'));
$(this).parent().next('.tooltip').animate({
opacity: 0
});
});
});
#dogs {
float: right;
margin-top: 5%;
background: black;
width: 150px;
height: 150px;
}
#d-list {
color: white;
direction: ltr;
float: right;
width: 60%;
height: 60%;
}
.dog-header {
font-size: 22px;
color: darkorange;
margin-bottom: 2em;
}
.tooltip {
color: black;
background-color: gray;
width: 120px;
height: 30px;
border-radius: 6px;
text-align: center;
padding-top: 5px;
opacity: 0;
position:relative;
top:-4.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="animals">
<div id="dogs"></div>
<div id="d-list">
<pre class="dog-header">dogs</pre>
<pre>white Husky</pre>
<p id="tooltiptext1" class="tooltip">Tooltip text1</p>
<pre>black Bull</pre>
<p id="tooltiptext2" class="tooltip">Tooltip text2</p>
<pre>brown Rex</pre>
<p id="tooltiptext3" class="tooltip">Tooltip text3</p>
</div>
</div>
Updated
This answer was written before the question was edited to show the intended markup/styling and before all the details were included. The code has been updated to work with that structure.
I think the simplest thing is just to create a configuration object to detail the varying bits, and then use common code for the rest. Here's one approach:
const configs = [
['d1', 'tooltiptext1', 'blue'],
['d2', 'tooltiptext2', 'green'],
['d3', 'tooltiptext3', 'red'],
];
configs.forEach(([id, tt, color]) => {
const dogs = document.getElementById('dogs');
const el = document.getElementById(id);
const tip = document.getElementById(tt);
el.onmouseover = (evt) => {
dogs.style.background = color
tip.style.visibility = "visible";
}
el.onmouseout = (evt) => {
dogs.style.background = "black";
tip.style.visibility = "hidden";
}
})
#dogs{float:right;margin-top:5%;background:#000;width:150px;height:150px}#d-list{color:#fff;direction:ltr;float:right;width:60%;height:60%}#tooltiptext1,#tooltiptext2,#tooltiptext3{color:#000;background-color:gray;width:120px;height:30px;border-radius:6px;text-align:center;padding-top:5px;visibility:hidden}
<div id="animals"> <div id="dogs"></div><div id="d-list"> <pre style="font-size:22px; color:darkorange">dogs</pre><br/> <pre>white Husky</pre> <p id="tooltiptext1">Tooltip text1</p><pre>black Bull</pre> <p id="tooltiptext2">Tooltip text2</p><pre>brown Rex</pre> <p id="tooltiptext3">Tooltip text3</p></div></div>
Obviously you can extend this with new rows really easily. And if you want to add more varying properties, you can simply make the rows longer. If you need to add too many properties to each list, an array might become hard to read, and it might become better to switch to {id: 'demo', tt: 'dem', color: 'blue'} with the corresponding change to the parameters in the forEach callback. (That is, replacing configs.forEach(([id, tt, color]) => { with configs.forEach(({id, tt, color}) => {.) But with only three parameters, a short array seems cleaner.
Older code snippet based on my made-up markup.
const configs = [
['demo', 'dem', 'blue'],
['dd', 'dem1', 'green']
];
configs.forEach(([id1, id2, color]) => {
const a = document.getElementById(id1)
const b = document.getElementById(id2)
a.onmouseover = (evt) => {
a.style.background = color
b.style.visibility = "visible";
}
a.onmouseout = (evt) => {
a.style.background = "black";
b.style.visibility = "hidden";
}
})
div {width: 50px; height: 50px; float: left; margin: 10px; background: black; border: 1px solid #666; color: red; padding: 10px; text-align: center}
#dem , #dem1{visibility:hidden;}
<div id="demo">demo</div>
<div id="dem">dem</div>
<div id="dd">dd</div>
<div id="dem1">dem1</div>
my way of seeing that => zero Javascript:
div[data-info] {
display: inline-block;
margin:80px 20px 0 0;
border:1px solid red;
padding: 10px 20px;
position: relative;
}
div[data-bg=blue]:hover {
background-color: blue;
color: red;
}
div[data-bg=green]:hover {
background-color: green;
color: red;
}
div[data-info]:hover:after {
background: #333;
background: rgba(0, 0, 0, .8);
border-radius: 5px;
bottom: 46px;
color: #fff;
content: attr(data-info);
left: 20%;
padding: 5px 15px;
position: absolute;
z-index: 98;
min-width: 120px;
max-width: 220px;
}
div[data-info]:hover:before {
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0px 6px;
bottom: 40px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}
<div data-info="Tooltip for A Tooltip for A" data-bg="blue">with Tooltip CSS3 A</div>
<div data-info="Tooltip for B" data-bg="green" >with Tooltip CSS3 B</div>

try to setTimeout to show and hide the balls

I tried to write a program to practice my js skills. There are 3 balls and they are hidden at first. I want the ball_1 shows up first, and after 1 sec, ball_1 disappears. Next, ball_2 shows up and after 1 sec it disappears; same logic goes with ball_3. When I run my code, the first two balls does not hide. I am not sure what is going wrong. The code below are the html, css, and js code that i wrote. Hope someone could help me out. Thank you in advance.
$(document).ready(function() {
var notes = ['ball_1', 'ball_2', 'ball_3'];
for (i = notes.length; i > 0; i--) {
var note = notes.shift();
$('#' + note).addClass('shown');
setTimeout(function() {
$('#' + note).removeClass('shown');
}, 1000);
}
});
#ball_1 {
width: 10px;
height: 10px;
background: #000000;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_2 {
width: 10px;
height: 10px;
background: #0000FF;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_3 {
width: 10px;
height: 10px;
background: #7FFF00;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_1,
#ball_2,
#ball_3 {
width: 10px;
height: 10px;
border: 2px solid #ccc;
border-radius: 50%;
}
.not_shown {
display: none;
}
.shown {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div id="ball">
<div id="ball_1" class="not_shown"></div>
<div id="ball_2" class="not_shown"></div>
<div id="ball_3" class="not_shown"></div>
</div>
In general never modify an array when iterating using a for loop. The shift method will remove the first item from the array thus modifying it's length. Instead do this:
$(document).ready(function() {
var notes = ['ball_1','ball_2','ball_3'];
var i; // You were declaring "i" in global namespace before. Don't do that.
for(i = 0; i < notes.length; i++){
var note = notes[i];
$('#' + note).addClass('shown');
setTimeout(function() {
$('#' + note).removeClass('shown');
},1000);
}
});
Also you will see from my note that you were defining "i" in the global namespace. It is never good to do that so always make sure to define your variables at the beginning of the function block if using "var".
EDIT: missed a semicolon
EDIT2: completely missed that i needed to change up the loop condition.
You are looking for an asnychronous play of events - first ball_1 shows up for 1 sec and after that ball_2 shows up for 1 sec and so forth.
Something like this won't work:
for( var i = 0; i < notes.length; i++){
$('#' + notes[i]).addClass('shown');
setTimeout(function() {
$('#' + notes[i]).removeClass('shown');
},1000);
}
because the timeouts will be registered one after the other in quick succession and all the balls will show up and hide in little over one second.
So you can create a callback and set the timeout for the next ball only after the previous ball has been shown fully for 1 sec - see demo below:
$(document).ready(function() {
var notes = ['ball_1', 'ball_2', 'ball_3'];
hideBall(notes,0);
});
function hideBall(notes,i) {
$('#' + notes[i]).addClass('shown');
hide(function() {
if(++i < notes.length) {
hideBall(notes,i);
}
}, notes[i]);
}
function hide(callback, note) {
setTimeout(function() {
$('#' + note).removeClass('shown');
callback();
}, 1000);
}
#ball_1 {
width: 10px;
height: 10px;
background: #000000;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_2 {
width: 10px;
height: 10px;
background: #0000FF;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_3 {
width: 10px;
height: 10px;
background: #7FFF00;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_1,
#ball_2,
#ball_3 {
width: 10px;
height: 10px;
border: 2px solid #ccc;
border-radius: 50%;
}
.not_shown {
display: none;
}
.shown {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div id="ball">
<div id="ball_1" class="not_shown"></div>
<div id="ball_2" class="not_shown"></div>
<div id="ball_3" class="not_shown"></div>
</div>
Hope this is what you need
$(document).ready(function() {
var notes = ['ball_1','ball_2','ball_3'];
for(i = notes.length; i > 0; i--){
var note = notes[i];
$('#' + note).addClass('shown');
hideBall(note, i)
}
});
function hideBall(note) {
setTimeout(function() {
$('#' + note).removeClass('shown');
},1000 * i);
}
#ball_1{
width: 10px;
height: 10px;
background: #000000;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_2{
width: 10px;
height: 10px;
background: #0000FF;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_3{
width: 10px;
height: 10px;
background: #7FFF00;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_1, #ball_2, #ball_3 {
width: 10px;
height: 10px;
border: 2px solid #ccc;
border-radius: 50%;
}
.not_shown {
display: none;
}
.shown {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id = "ball">
<div id = "ball_1" class = "not_shown"></div>
<div id = "ball_2" class = "not_shown"></div>
<div id = "ball_3" class = "not_shown"></div>
</div>
What you are trying won't work as it will run the for loop all in one go, setting up 3x timeouts.
try something like this
jQuery(document).ready(function($) {
function myBallLoop(){
// increment as needed
if(typeof note == 'undefined') {
var note = 1;
} else if (note == 3){
break; // end loop
} else {
note ++;
}
// show current ball qickly
$('#ball_' + note).show('fast', function(){
// call back after show event
// hide current ball after 1 sec
r = setTimeout(function(){$('#ball_' + note).hide()}, 1000);
// self call function after 2 seconts
t = setTimeout(function(){myBallLoop();, 2000}
});
}
// loop start
myBallLoop();
});
Take advantage of what jquery gives you.
Iterate using $.each is also the same as ES5's forEach. Using delay method to delay a function of adding classes is similar to setTimeout.
$(document).ready(() => {
var notes = ['ball_1','ball_2','ball_3'];
let showBalls = (i, item) => {
$('#' + item).delay(i * 1000).queue(() => {
$('#' + item).addClass('shown');
$('#' + notes[i - 1]).removeClass('shown').clearQueue();
});
}
$.each(notes, (i, item) => {
showBalls(i, item);
});
});
#ball_1{
width: 10px;
height: 10px;
background: #000000;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_2{
width: 10px;
height: 10px;
background: #0000FF;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_3{
width: 10px;
height: 10px;
background: #7FFF00;
border: 2px solid #ccc;
border-radius: 50%;
}
#ball_1, #ball_2, #ball_3 {
width: 10px;
height: 10px;
border: 2px solid #ccc;
border-radius: 50%;
}
.not_shown {
display: none;
}
.shown {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id = "ball">
<div id = "ball_1" class = "not_shown"></div>
<div id = "ball_2" class = "not_shown"></div>
<div id = "ball_3" class = "not_shown"></div>
</div>

Categories