Traverse an array of divs by clicking - javascript

I have a page with 'previous' and 'next' buttons to traverse an array of divs.
Clicking next button will copy the next div indexed by the var 'click' into #place's innerHTML.
Clicking previous button will copy the previous div as you can imagine.
The problem is, when I click the button alternately(click previous and next and previous then next...), the div would remain as it is and will not change to the next or previous div, unless I click the same button twice.
A simple alert message would confirm the functions are executing when alternately clicked. What's going on? Thank you.
var clicks = 0;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
function g() {
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
clicks += 1;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="f()" style="width: 10%;">Previous</button>
<button type="button" onclick="g()" style="width: 10%;">Next</button>

You need to consistently add and subtract in the same place.
But no need for two functions
NOTE: for an additional improvement, just hide and show instead of inserting code into the DOM
var clicks = -1, // necessary for initial next clicking
pageDivs = document.getElementsByClassName("plist"),
displayPanel = document.getElementById("place"),
len = pageDivs.length;
function copyIt(direction) {
clicks += direction;
if (clicks >= len) clicks = 0;
else if (clicks < 0) clicks = len - 1;
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="copyIt(-1)" style="width: 10%;">Previous</button>
<button type="button" onclick="copyIt(1)" style="width: 10%;">Next</button>

When you click Next, increment your counter first. You're doing it after, which is causing the issue.
Define your function f like this:
function g() {
clicks += 1; // increment at the start
...
}
var clicks = 0;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
function g() {
clicks += 1;
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="f()" style="width: 10%;">Previous</button>
<button type="button" onclick="g()" style="width: 10%;">Next</button>

The problem was clicks counter was getting updated after updating the innerHTML.
Moving that to top fixed the issue.
$(document).ready(function() {
var clicks = -1;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
$("#prev").on("click", f);
$("#next").on("click", g);
function g() {
clicks += 1;
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
});
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button id="prev" type="button" style="width: 10%;">Previous</button>
<button id="next" type="button" style="width: 10%;">Next</button>

Related

Animate one paragraph at a time

I have a div with several paragraphs inside of it. I want each of these paragraphs to fade in one after the other. I can do that with the following code. However, since I will have many more divs with many other paragraphs, each with their unique class names, I wonder if there is an easier way to achieve this, without keep copy-pasting the code, changing the class names each time.
$('.line1').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line2').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line3').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line4').css('visibility','visible').hide().fadeIn(1000);
});
});
});
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>
Class names don't matter here, you can use .line or .line{i} or anything else, as long as there is an easy way to select all of them.
If you want different class names you could use .div1 > p in place of .line in the code.
If slight (+/- few milliseconds) innaccuracies aren't an issue, you could use setTimeout for this.
$(".line").each(function (i) {
$(this).css("opacity", 0);
setTimeout(() => {
$(this).animate({ opacity: 1 }, 1000);
}, 1000 * i);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line">Text 01</p>
<p class="line">Text 02</p>
<p class="line">Text 03</p>
<p class="line">Text 04</p>
</div>
You can also use .delay instead of setTimeout.
$(".line").each(function (i) {
$(this).css("opacity", 0).delay(1000 * i).animate({ opacity: 1 }, 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line">Text 01</p>
<p class="line">Text 02</p>
<p class="line">Text 03</p>
<p class="line">Text 04</p>
</div>
You can use delay to delay subsequent fadeIns, like this:
for (var n = 1; n <= 4; ++n) {
$(".line" + n).css('visibility','visible').hide().delay(1000 * (n - 1)).fadeIn(1000);
}
Live Example:
for (var n = 1; n <= 4; ++n) {
$(".line" + n).css('visibility','visible').hide().delay(1000 * (n - 1)).fadeIn(1000);
}
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>
Or with any list of class names:
["line1", "line2", "line3", "line4"].forEach(function(cls, index) {
$("." + cls).css('visibility','visible').hide().delay(1000 * index).fadeIn(1000);
});
Live Example:
["line1", "line2", "line3", "line4"].forEach(function(cls, index) {
$("." + cls).css('visibility','visible').hide().delay(1000 * index).fadeIn(1000);
});
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>

How to make this multi-slide panel loop indefinitely

I have three panels that have just text or text and images that I want to loop indefinitely, and be scalable from 1-1000 slides.
I have the following markup:
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>1.1</h1>
<p>slide 1.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>1.2</h1>
<p>slide 1.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>1.3</h1>
<p>slide 1.3</p>
<p class="datetime"></p>
</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>2.1</h1>
<p>slide 2.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>2.2</h1>
<p>slide 2.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>2.3</h1>
<p>slide 2.3</p>
<p class="datetime"></p>
</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>3.1</h1>
<p>slide 3.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>3.2</h1>
<p>slide 3.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>3.3</h1>
<p>slide 3.3</p>
<p class="datetime"></p>
</div>
</div>
</div>
And the following script:
<script>
$(document).ready(function() {
var items = $(".mb-panel"),
currentItem = items.filter(".active");
window.setInterval( function() {
var nextItem = currentItem.next();
currentItem.removeClass("active");
if( nextItem.length ) {
currentItem = nextItem.addClass("active");
} else {
currentItem = items.first().addClass("active");
}
}, 5000);
});
</script>
Unfortunately I am ending up with something like this:
Essentially, the first run of the panels work, but when it gets to the loops it stops for the other panels apart from column 1. I will be opening this up to allow the users to add as many notices per panel as they need, but require it to loop back to the beginning for each column once it reaches the last slide.
You have to pick the exact element with .eq(index) and change the index, depending - if it reached the max allowed length.
$('.mb-slider').each(function(){ // looping for each slider block
let panels = $(this).find('.mb-panel'); // collecting current slides
let len = panels.length;
let index = 0;
setTimeout(function loop(){
panels.eq(index).removeClass('active');
index = (index == len - 1) ? 0 : index + 1; // Google → Ternary operator
panels.eq(index).addClass('active');
setTimeout(loop, 1000);
}, 1000);
});
.mb-panel {
display: none;
border: 2px solid orange;
margin: 5px;
padding: 5px;
}
.mb-panel.active { display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">1-1</div>
<div class="mb-panel">1-2</div>
<div class="mb-panel">1-3</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">2-1</div>
<div class="mb-panel">2-2</div>
<div class="mb-panel">2-3</div>
<div class="mb-panel">2-4</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">3-1</div>
<div class="mb-panel">3-2</div>
<div class="mb-panel">3-3</div>
<div class="mb-panel">3-4</div>
<div class="mb-panel">3-5</div>
</div>
</div>
I've used self-calling setTimeout chain just because like that trick. Here you can use setInterval as well. But in some cases, it make sense - not to call function repetitively, while the previous step haven't completed yet.
Translated into native JS ( find 10 differences :D ):
let slider = document.querySelectorAll('.mb-slider');
for( let i = 0; i < slider.length; i++ ){
let panels = slider[i].querySelectorAll('.mb-panel');
let len = panels.length;
let index = 0;
setTimeout(function loop(){
panels[index].classList.remove('active');
index = (index == len - 1) ? 0 : index + 1;
panels[index].classList.add('active');
setTimeout(loop, 1000);
}, 1000);
}
.mb-panel {
display: none;
border: 2px solid orange;
margin: 5px;
padding: 5px;
}
.mb-panel.active { display: block; }
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">1-1</div>
<div class="mb-panel">1-2</div>
<div class="mb-panel">1-3</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">2-1</div>
<div class="mb-panel">2-2</div>
<div class="mb-panel">2-3</div>
<div class="mb-panel">2-4</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">3-1</div>
<div class="mb-panel">3-2</div>
<div class="mb-panel">3-3</div>
<div class="mb-panel">3-4</div>
<div class="mb-panel">3-5</div>
</div>
</div>

How to target a specific slider using javascript when there is more than one

I have 3 sliders on my page I'm a building but i am just curious to know the best way to go about targeting only the active one so the javascript below would work for all of them. Everything seems to work fine if i disable the other 2 sliders. First time I have done something like this.
I'm guessing my javascript selectors may need to change some what to get it to work.
Appreciate any advice on the best way forward.
var sliderSlide = document.querySelectorAll('.slider__slide');
var nextSlide = document.querySelector('.slider__button--next');
var previousSlide = document.querySelector('.slider__button--previous');
var currentSlide = 0;
var currentSlideImg = 0;
//Reset slides
function resetSlides() {
for (var s = 0; s < sliderSlide.length; s++) {
sliderSlide[s].classList.remove('active');
}
for (var i = 0; i < sliderSlideImg.length; i++) {
sliderSlideImg[i].classList.remove('active');
}
}
//Start slides
function startSlide() {
resetSlides();
sliderSlide[0].classList.add('active');
sliderSlideImg[0].classList.add('active');
}
//Previous slide
function slidePrevious() {
resetSlides();
sliderSlide[currentSlide - 1].classList.add('active');
currentSlide--;
sliderSlideImg[currentSlideImg - 1].classList.add('active');
currentSlideImg--;
}
previousSlide.addEventListener('click', function() {
if (currentSlide === 0 && currentSlideImg === 0) {
currentSlide = sliderSlide.length;
currentSlideImg = sliderSlideImg.length;
}
slidePrevious();
});
//Next slide
function slideNext() {
resetSlides();
sliderSlide[currentSlide + 1].classList.add('active');
currentSlide++;
sliderSlideImg[currentSlideImg + 1].classList.add('active');
currentSlideImg++;
}
nextSlide.addEventListener('click', function() {
if (currentSlide === sliderSlide.length - 1 && currentSlideImg === sliderSlideImg.length - 1) {
currentSlide = -1;
currentSlideImg = -1;
}
slideNext();
});
<div class="slider slider--1 active">
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__buttons">
<span class="slider__button--previous">Previous</span>
<span class="slider__button--next">Next</span>
</div>
</div>
<div class="slider slider--2">
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__buttons">
<span class="slider__button--previous">Previous</span>
<span class="slider__button--next">Next</span>
</div>
</div>
<div class="slider slider--3">
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__slide">
<p class="slider__text">some text</p>
</div>
<div class="slider__buttons">
<span class="slider__button--previous">Previous</span>
<span class="slider__button--next">Next</span>
</div>
</div>
You can create loop over .slider and then use querySelector on each item, that way you will have variables for each slider
Array.from(document.querySelectorAll('.slider')).forEach(function(slider) {
var sliderSlide = slider.querySelectorAll('.slider__slide');
var nextSlide = slider.querySelector('.slider__buttons--next');
var previousSlide = slider.querySelector('.slider__buttons--previous');
...
});
or if you prefer for loop:
var sliders = document.querySelectorAll('.slider');
for (var i = 0; i < sliders.length; ++i) {
var slider = sliders[i];
...
}
document.querySelector('.slider--3 .slider__slide')
but I would recommend to put id's on your sliders and then select
document.querySelector('#slider--3')

unhide elements on click

I've got a little issue with JavaScript. I am not sure if my code not work, or if I am about lunch it wrong way.
<title>Untitled Document</title>
<script language="javascript">
function Unhide()
{
var item = document.getElementsByTagName('p');
for (x; x > item.length; x+1)
{
if (item.item(x).getAttribute('hidden') == ('true'))
{
item.item(x).setAttribute('hidden', 'false');
}
else
{
item.item(x).setAttribute('hidden', 'true');
}
}
}
</script>
</head>
<body>
<div class="Level1">
<p class="Menu">Home</p>
<div class="Level2">
<p class="Menu">Artykół 1</p>
<p class="Menu">Artykół 2</p>
<p class="Menu">Artykół 3</p>
<p class="Menu">Artykóły Autorskie</p>
</div>
<p class="Menu">Dziennik</p>
<p class="Menu">Archiwum</p>
<div class="Level2">
<p class="Menu"><a onclick="Unhide()">Ostatni Tydzień</a></p>
<div class="Level3">
<p class="Menu" hidden="true">Art1</p>
<p class="Menu" hidden="true">Art2</p>
</div>
</div>
</div>
</body>
Could you give me any tip, how to figure it out?
function Unhide()
{
var items = document.getElementsByTagName('p');
for (var x in items)
{
if (x.style.display == 'none')
{
x.style.display = '';
}
else
{
x.style.display = 'none';
}
}
}
items is an array of elements. iterate through them with a for loop and set style.display property.
Reference: http://www.w3schools.com/jsref/prop_style_display.asp
http://davidwalsh.name/html5-hidden
<script>
function Unhide()
{
var items = document.getElementsByTagName('p');
for ( var x = 0; x < items.length; x++)
{
var item = items[x];
if (item.getAttribute('hidden') == 'true')
{
item.setAttribute('hidden', 'false');
}
else
{
item.setAttribute('hidden', 'true');
}
}
}
</script>
<div class="Level1">
<p class="Menu">Home</p>
<div class="Level2">
<p class="Menu">Artykół 1</p>
<p class="Menu">Artykół 2</p>
<p class="Menu">Artykół 3</p>
<p class="Menu">Artykóły Autorskie</p>
</div>
<p class="Menu">Dziennik</p>
<p class="Menu">Archiwum</p>
<div class="Level2">
<p class="Menu"><a onclick="Unhide()">Ostatni Tydzień</a></p>
<div class="Level3">
<p class="Menu" hidden="true">Art1</p>
<p class="Menu" hidden="true">Art2</p>
</div>
</div>
</div>
For me works:
item.attr('hidden', false);
but only if the item's attribute looks like:
hidden="true"
not only "hidden".

on keypress display one div and other should hide

when i press any key i need to display first div "d1" others hide and i press any key again it display secound div "d2" others hide and press any key again it display third div "d3" others hide ..till six div..not repeating the process again.
<div class="objects" id="d1">
<img src="images/d1.jpg" />
</div>
<div class="objects" id="d2">
<img src="images/d2.jpg" />
</div>
<div class="objects" id="d3">
<img src="images/d3.jpg" />
</div>
.......
.......
<div class="objects" id="d6">
<img src="images/d6.jpg" />
</div>
when i press any key i need to display 1st div then i press any key display 2nd div..till 6th div..
how to do it in javascript?
<html>
<head>
<style type="text/css">
.Div
{
display: none;
}
</style>
<script type="text/javascript">
var i = 0;
function KeyHandler() {
i++;
var divs = document.getElementsByClassName("Div");
for (var div = 0; div < divs.length - 1; div++) {
divs[div].style.display = 'none';
}
var ele = document.getElementById("d" + i);
ele.style.display = "block";
}
</script>
</head>
<body onkeyup="KeyHandler()">
Press any key..
<div id="d1" class="Div">
<h5>
Div1</h5>
</div>
<div id="d2" class="Div">
<h5>
Div2</h5>
</div>
<div id="d3" class="Div">
<h5>
Div3</h5>
</div>
<div id="d4" class="Div">
<h5>
Div4</h5>
</div>
<div id="d5" class="Div">
<h5>
Div5</h5>
</div>
<div id="d6" class="Div">
<h5>
Div6</h5>
</div>
</body>
</html>
Try this out
var currentVisible = 0;
document.onkeyup = function() {
// First hide all by class 'objects'
// Requires IE9+ FF3+, others are supported
var objs = document.getElementsByClassName('objects');
for (var i=0; i<objs.length; i++) {
objs[i].style.display = 'none';
}
// Show the one we are supposed to show
if (++currentVisible == 7)
currentVisible = 1;
var el = document.getElementById('d' + currentVisible);
el.style.display = 'block';
}
100% untested tho, since I have no access to any fiddle atm.

Categories