I'm having a problem returning elements from my object array.
I have a slideshow in which an image (photo of the book) and a text (title of the book) must appear.
For the creation of the slideshow I have no problems because I am using querySelectors as shown in the code below.
The problem lies in showing the images with their titles.
In the first part of the code I have an array of objects that gives me the total number of elements present and based on this I create the slideshow (to create the scroll points and the "container" to contain the images) and then subsequently I call the function myFunction with id argument to return the single element (identified by an id).
What I notice is that the urls with the ids are returned to me, but the url with that id is returned multiple times (so it gets copied) and not just once as expected; I should have only 4 url in total (each with the specific id of the element, since only 4 elements). The same happens when I return the array of objects, it does not return 4 but many more.
Referring to url1:
If I click on the arrows of the slideshow I only receive one image, the other images I do not receive.
I have no mistakes.
var slideIndex = 1;
var outerXmlhttp = new XMLHttpRequest();
var url = "https://wjko8t4509.execute-api.us-east-2.amazonaws.com/books";
outerXmlhttp.onreadystatechange = function () {
var innerXmlhttp;
if (this.readyState == 4 && this.status == 200) {
var allbook = JSON.parse(this.responseText);
for (var i = 0, len = allbook.Items.length; i < len; i++) {
id = allbook.Items[i].id
showSlides(slideIndex);
myFunction(id);
}
}
};
outerXmlhttp.open("GET", url, true);
outerXmlhttp.send();
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides fade");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) { slideIndex = 1 }
if (n < 1) { slideIndex = slides.length }
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex - 1].style.display = "block";
dots[slideIndex - 1].className += " active";
}
function myFunction(id) {
var url1 = "https://wjko8t4509.execute-api.us-east-2.amazonaws.com/books/" + id;
innerXmlhttp = new XMLHttpRequest();
innerXmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var myArr = JSON.parse(this.responseText);
document.getElementById("img").src = "book_img/" + myArr.Item.immagine;
document.getElementById("title").innerHTML = `${myArr.Item.titolo}`;
}
};
innerXmlhttp.open("GET", url1, true);
innerXmlhttp.send();
}
<div class="slideshow-container" id="slideshow">
<a class="prev" onclick="plusSlides(-1)">❮</a>
<a class="next" onclick="plusSlides(1)">❯</a>
</div>
<br>
<div id="punt" style="text-align:center">
</div>
EDIT
Array allbook with error:
Several things I noticed:
Doesn't define the id
As you state, the data structure from api will look like {"Item":{"marca":"Love","titolo":"You will be missed indefinitely","id":"123456","immagine":"ind.jpg","data":"27/11/2021"}}, but are trying to loop over all the item inside item which will be incorrect, you should loop over allbook.
It should be allbook[i].Items.length instead of allbook.Items.length
var slideIndex = 1;
let id;
var outerXmlhttp = new XMLHttpRequest();
var url = "https://wjko8t4509.execute-api.us-east-2.amazonaws.com/books";
outerXmlhttp.onreadystatechange = function() {
var innerXmlhttp;
if (this.readyState == 4 && this.status == 200) {
var allbook = JSON.parse(this.responseText);
for (var i = 0, len = allbook.length; i < len; i++) {
id = allbook[i].Items.id
document.querySelector('.slideshow-container').innerHTML += `
<div class="mySlides fade">
<div class="numbertext">${i+1}/${allbook[i].Items.length}</div>
<img id="img" src onerror="this.onerror=null; this.src=myFunction(${id});" style="width:100%">
<div class="text" id="title" onclick="myFunction(${id})"></div>
</div>`;
document.querySelector('#punt').innerHTML += `
<span class="dot" onclick=currentSlide(${i+1})></span>`;
myFunction(id);
}
showSlides(0);
}
};
outerXmlhttp.open("GET", url, true);
outerXmlhttp.send();
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides fade");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) {
slideIndex = 1
}
if (n < 1) {
slideIndex = slides.length
}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex - 1].style.display = "block";
dots[slideIndex - 1].className += " active";
}
function myFunction(id) {
var url1 = "https://wjko8t4509.execute-api.us-east-2.amazonaws.com/books/" + id;
innerXmlhttp = new XMLHttpRequest();
innerXmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myArr = JSON.parse(this.responseText);
document.getElementById("img").src = "book_img/" + myArr.Item.immagine;
document.getElementById("title").innerHTML = `${myArr.Item.titolo}`;
}
};
innerXmlhttp.open("GET", url1, true);
innerXmlhttp.send();
}
<div class="slideshow-container" id="slideshow">
<a class="prev" onclick="plusSlides(-1)">❮</a>
<a class="next" onclick="plusSlides(1)">❯</a>
</div>
<br>
<div id="punt" style="text-align:center">
</div>
Related
I have tried reading up on my problem and gather event delegation and/or event listeners seem like what I need to do. However, I am confused on the implementation for my script so help would be appreciated.
I can see the problem is probably that the showSlides function references mySlides and grid-box which are dynamically created elements that have not yet been created when the function is called. Is that right? It is a slideshow viewer for context which worked when I did not dynamically create the elements.
The top section is when I did not make the HTML elements dynamically and it worked.
var num_slides = 3
for (let i=1; i<num_slides; i++) {
var $my_slides= $("<div><img style = 'width:100%'/></div>", {"class": "mySlides"});
$('#slideshow-container').prepend($my_slides);
var $grid_box = $("<div><img/></div>", {"class": "grid-box"});
// $(document).on('click','div.grid-box',currentSlide(i)); // I've tried this
$grid_box.on('click', function(event){currentSlide(i)});
$('#picture-grid').append($grid_box);
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var gridboxes = document.getElementsByClassName("grid-box");
if (n > slides.length) {slideIndex = 1}
if (n < 1) {slideIndex = slides.length}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < gridboxes.length; i++) {
gridboxes[i].classList.remove("active");
}
slides[slideIndex-1].style.display = "block";
gridboxes[slideIndex-1].className += " active";
}
var slideIndex = 1;
showSlides(slideIndex);
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
document.onkeydown = function(event) {switch (event.keyCode) {case 37:currentSlide(slideIndex-1);break;case 39:currentSlide(slideIndex+1);break;}};
function onChangeMap(){
const met_var = this.value;
const basepath = '/home/2015_casestudy/';
var startYear = 2014;
var startMon = 1;
document.querySelectorAll('.mySlides img').forEach(function(img, index){
if ((startMon + index) % 12 == 0) {
monthNum = 12;
yearNum = startYear + ((startMon+index) / 12) - 1;
} else {
monthNum = (startMon+index) % 12
yearNum = Math.floor(startYear + ((startMon+index) / 12));
}
var formattedMonthNum = ("0" + monthNum.toString()).slice(-2);
var dateTag = yearNum.toString() + formattedMonthNum
if ((met_var == 'sm_anom_1month' || met_var == 'sm_pct_1month') && (yearNum == 2014)) {
img.src = basepath + "blank.png";
} else {
img.src = basepath + met_var + '_' + dateTag + "?" + new Date().getTime();}
})
document.querySelectorAll('.picture-grid img').forEach(function(img, index){
if ((startMon + index) % 12 == 0) {
monthNum = 12;
yearNum = startYear + ((startMon+index) / 12) - 1;
} else {
monthNum = (startMon+index) % 12
yearNum = Math.floor(startYear + ((startMon+index) / 12));
}
var formattedMonthNum = ("0" + monthNum.toString()).slice(-2);
var dateTag = yearNum.toString() + formattedMonthNum
if ((met_var == 'sm_anom_1month' || met_var == 'sm_pct_1month') && (yearNum == 2014)) {
img.src = basepath + "blank.png";
} else {
img.src = basepath + met_var + '_' + dateTag + "?" + new Date().getTime();}
})
}
window.addEventListener("load",
initPage);
function initPage(){
document.querySelector('#myFormSelect').addEventListener("change", onChangeMap);
document.querySelector('#myFormSelect').dispatchEvent(new Event('change'));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"
type="text/javascript" charset="utf-8"></script>
<select id="myFormSelect" name="met variables">
<option value="temp" selected='selected'>Temp</option>
<option value="rain">Rainfall</option>
</select>
<div id="slideshow-container" class="slideshow-container">
<a class="prev" onclick="plusSlides(-1)">❮</a>
<a class="next" onclick="plusSlides(1)">❯</a>
</div>
<div id="picture-grid" class="picture-grid"></div>
Where you are created the 's might be running at the same time as the code you have.
Make sure they are created before the code you have. Also, appears that the code is referring to div's with a specific class.
When you are creating the divs in a loop, don't forget to add the appropriate class to match what the code is trying to find.
The code is unable to find the div's either because they are created at the wrong time, or they just don't have the correct id/class names.
var $grid_box = $("<div><img/></div>", {"class": "grid-box"});
Here was your working version: with the missing class name +.correct onClick calls inside the append
var $grid_box = $("<div class='grid-box' onclick='javascript:currentSlide(' + i + ')'><img/></div>');
I have a slideshow in JS and html which works fine until I use a variable stored in localStorage to define which slide to start from when the page refresh/reload.
What works:
- after the page has reloaded, the slideshow starts from the desired slide;
- after the page has reloaded and after the first command forward, the slideshow begins to work correctly.
What does not work:
- after the page has reloaded, moving the slideshow forward for the first time always makes the slideshow begins from the first slide;
- after the page has reloaded, moving the slideshow backward for the first time always makes the variable in the local storage undefined and the slides disappear.
I think the issue is with the showSlides(), but I cannot understand what it is.
Here is my code so far:
// Set local storage
var slideIx = localStorage.getItem('slideIndex');
if (slideIx === null) {
slideIx = 1;
}
var slideIndex = slideIx;
showSlides(slideIndex);
// Next/previous controls
function plusSlides(n) {
showSlides(slideIndex += n);
localStorage.setItem('slideIndex', slideIndex);
console.log(localStorage.getItem('slideIndex'));
}
// Thumbnail image controls
function currentSlides(n) {
showSlides(slideIndex = n);
localStorage.setItem('slideIndex', slideIndex);
console.log(localStorage.getItem('slideIndex'));
}
// Show slides
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) {
slideIndex = 1;
}
if (n < 1) {
slideIndex = slides.length;
}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " active";
}
// Arrows control
document.onkeydown = function(e) {
e = e || window.event;
if (e.keyCode == '37') {
e.preventDefault();
plusSlides(-1); //left <- show Prev image
} else if (e.keyCode == '39') {
e.preventDefault();
plusSlides(1); // right -> show next image
}
};
Thank you very much for any hint you might have(!).
I think the issue is that you need to convert string to number before you operate with it. The value retrieved from localStorage is always of a String type, while you expect number in your code. Try this:
var slideIx = Number(localStorage.getItem('slideIndex') || 1);
(then next check for null is not needed).
I succeeded the slideShow to show the last picture after refreshing the page by the next code (you can see it here https://jsfiddle.net/mu9otwcy/1/):
var myIndex;
if(localStorage.getItem('slideIx') === null){
myIndex = 0;
}
else{
myIndex = localStorage.getItem('slideIx') -1;
}
console.log(myIndex);
carousel();
function carousel() {
var i;
var x = document.getElementsByClassName("mySlides");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
myIndex++;
console.log(myIndex);
if (myIndex > x.length) {
myIndex = 1;
}
x[myIndex-1].style.display = "block";
var slideIx = localStorage.setItem('slideIx',myIndex);
setTimeout(carousel, 3000);
}
I'm trying to add a play and pause button to my image viewer. I have next and prev buttons working fine but would love a play and pause button integrated into my JavaScript.
Can anyone please help out?
Here is a working demo via link Demo
Script:
<div class="w3-container">
</div>
<div class="w3-content" style="max-width:800px">
<img class="mySlides" src="http://www.chorleyweather.com/forecast-charts/ecmwfcharts/na500hpa_1.png" style="width:95%"></li>
<img class="mySlides" src="http://www.chorleyweather.com/forecast-charts/ecmwfcharts/na500hpa_2.png" style="width:95%"></li>
</div>
<div class="w3-section">
<button class="w3-btn" onclick="plusDivs(-1)"><h6> Prev Image<h6/></button>
<button class="w3-btn" onclick="plusDivs(1)"><h6>Next Image<h6/> </button><h6/>
</div>
</div>
<script>
var slideIndex = 1;
showDivs(slideIndex);
function plusDivs(n) {
showDivs(slideIndex += n);
}
function currentDiv(n) {
showDivs(slideIndex = n);
}
function showDivs(n) {
var i;
var x = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("demo");
if (n > x.length) {slideIndex = 1}
if (n < 1) {slideIndex = x.length}
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" w3-red", "");
}
x[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " w3-red";
}
window.onload = function() {
var image = document.getElementById("img");
function updateImage() {
image.src = image.src.split("?")[0] + "?" + new Date().getTime();
}
setInterval(updateImage, 1000);
}
</script>
A way would be with a boolean which determines if it should be playing or not, and a button that changes the value of that boolean.
button:
<button id="playpause"></button>
updated window.onload = function
window.onload = function() {
var image = document.getElementById("img");
var playing = true;
function updateImage() {
if (playing) {
image.src = image.src.split("?")[0] + "?" + new Date().getTime();
}
}
var playpause = document.getElementById("playpause");
playpause.onclick = function() {
if (playing) {
playing = false;
} else {
playng = true;
}
};
setInterval(updateImage, 1000);
}
Didn't try this, but it should do the trick.
I wanna add 2 slideshow in one single webpages so this is the code for 1 slide show
<script>
var slideIndex = 0;
var slides = document.getElementsByClassName("mySlides1");
showSlides();
function showSlides() {
var i;
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
slideIndex++;
if (slideIndex> slides.length) {slideIndex = 1}
slides[slideIndex-1].style.display = "block";
setTimeout(showSlides, 3000); // Change image every 5 seconds
}
function currentSlide(no) {
var i;
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
slideIndex = no;
slides[no-1].style.display = "block";
function plusSlides(n) {
var newslideIndex = slideIndex + n;
if(newslideIndex < 6 && newslideIndex > 0){
currentSlide(newslideIndex);
}
}
</script>
then I wanted to add another slideshow so I add mySlides2
var slides = document.getElementsByClassName("mySlides1", "mySlides2");
but it's not working .
Get your slide elements into two HTMLCollections then combine them into one array.
var slide1 = document.getElementsByClassName("mySlides1");
var slide2 = document.getElementsByClassName("mySlides1");
var slides = [];
for(var i = 0; i < slide1.length; i++){
slides[i] = slide1[i];
}
for(var length = slide1.length, i = length; i < slide2.length+length; i++ ){
slides[i] = slide2[i - length];
}
//showSlides();
Hey I have a span element that I am creating with JS and I want it to also have an onclick event that calls a function. I am referring to the function fillSlides() and dotFunction(), but I will show all of my functions.
function openModal() {
document.getElementById('myModal').style.display = "block";
}
function closeModal() {
document.getElementById('myModal').style.display = "none";
}
var slideIndex = 1;
showSlides(slideIndex);
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function fillSlides(modalID){
var container = document.getElementsByClassName("modal-content");
var bcontainer = document.getElementById("myModal");
var dcontainer = document.createElement('dot_content');
var slides = {
"1": ["Images/LS_01.jpg", "Images/LS_02.jpg", "Images/LS_03.jpg", "Images/LS_04.jpg" ],
"2": ["Images/LS_05.jpg", "Images/LS_06.jpg", "Images/LS_07.jpg", "Images/LS_08.jpg" ],
"3": ["Images/img1.jpg", "Images/img2.jpg", "Images/img3.jpg", "Images/2.jpg" ]
};
var modal_num = modalID.getAttribute('data-modal');
//alert(slides[modal_num].length);
for (var i = 0 ; i < slides[modal_num].length; i++) {
var the_divs = document.createElement('div');
var s_img = document.createElement('img');
var s_dot = document.createElement('span');
the_divs.className = 'mySlides';
s_img.src = slides[modal_num][i];
the_divs.appendChild(s_img);
container[0].appendChild(the_divs);
dcontainer.className = 'dot-content';
dcontainer.id = 'dot_content';
s_dot.className = 'demo';
dcontainer.appendChild(s_dot);
container[0].appendChild(dcontainer);
s_dot.onclick = currentSlide(i);
}
}
/*
function clearSlides(){
var myNode = document.getElementById("modal_content");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
}*/
function clearSlides() {
var myNode = document.getElementById("modal_content");
//use spread operator to convert nodelist to array for using
// array methods
var c = [...myNode.children];
c.forEach(function(item, index) {
console.log(item)
// check if the current element have a class using 'contains'
if (!item.classList.contains('elbutton')) {
//use remove method to remove the element
item.remove();
}
})
}
/*
function clearSlides(){
var myNode = document.getElementById("modal_content");
var childNodes = myNode.childNodes;
for(var i=0; i<childNodes.length; i++){
if(childNodes[i].className === "mySlides"){
myNode.removeChild(childNodes[i])
}
}
}
*/
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("demo");
var captionText = document.getElementById("caption");
if (n > slides.length) {slideIndex = 1}
if (n < 1) {slideIndex = slides.length}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " active";
//captionText.innerHTML = dots[slideIndex-1].alt;
}
function dotfunction() {
var slides = document.getElementsByClassName("mySlides");
var dotfunctionadder = document.getElementsByClassName("demo");
for (var i = 0; i < slides.length; i++) {
dotfunctionadder.onclick = currentSlide(i);
}
}
<body id="modal-2">
<h2 id="title" style="text-align:center">hellkkko</h2>
<div class="row">
<div class="column">
<img id="modal-1" src="https://www.yosemitehikes.com/images/wallpaper/yosemitehikes.com-bridalveil-winter-1200x800.jpg" style="max-width:100%" data-modal="1" onclick="fillSlides(this); openModal(); currentSlide(1); " class="hover-shadow cursor">
</div>
</div>
<div class="row">
<div class="column">
<img id="modal-2" src="https://www.yosemitehikes.com/images/wallpaper/yosemitehikes.com-bridalveil-winter-1200x800.jpg" style="max-width:100%" data-modal="2" onclick="fillSlides(this); openModal(); currentSlide(1); " class="hover-shadow cursor">
</div>
</div>
<div class="row">
<div class="column">
<img id="modal-3" src="https://www.yosemitehikes.com/images/wallpaper/yosemitehikes.com-bridalveil-winter-1200x800.jpg" style="max-width:100%" data-modal="3" onclick="fillSlides(this); openModal(); currentSlide(1); " class="hover-shadow cursor">
</div>
</div>
<div id="myModal" class="modal">
<span class="close cursor" onclick="clearSlides(); closeModal();">×</span>
<div class="modal-content" id="modal_content">
<a class="elbutton prev" onclick="plusSlides(-1)">❮</a>
<a class="elbutton next" onclick="plusSlides(1)">❯</a>
</div>
</div>
I tried calling dot function from the onclick of a different element (because the span I want to add it to is being created with JS) within the html but nothing happened. How do I do this and what am I doing wrong?
Try putting your script in the head section if you want to bind click handler inside html.
Below is the fiddle that i tested with your chanegs.
<head>
<script>
function openModal() {
document.getElementById('myModal').style.display = "block";
}
function closeModal() {
document.getElementById('myModal').style.display = "none";
}
..........
</script>
<head>
"https://jsfiddle.net/ajaygandhi06/smca56fv/"
It works when you put your js inside head tag.