JS Dom manipulation issue - javascript

So i am creating a carousel generator framework and I want to make the implementation as simple as possible for the user. The developer is supposed to add
images without caring about design/responsiveness. The framework must take every image and insert it in a div with the classname of "slide". In this case from this code:
<div id="album" class="album">
<img src="./assets/img1.jpeg" alt="img1">
<img src="./assets/img2.jpeg" alt="img2">
<img src="./assets/img3.jpeg" alt="img3">
<img src="./assets/img4.jpeg" alt="img4">
<img src="./assets/img5.jpeg" alt="img5">
<img src="./assets/img6.jpeg" alt="img6">
</div>
the framework should generate this:
<div class="slide">
<img src="./assets/img1.jpeg" alt="img1">
</div>
<div class="slide">
<img src="./assets/img2.jpeg" alt="img2">
</div>
<div class="slide">
<img src="./assets/img3.jpeg" alt="img3">
</div>
<div class="slide">
<img src="./assets/img4.jpeg" alt="img4">
</div>
<div class="slide">
<img src="./assets/img5.jpeg" alt="img5">
</div>
<div class="slide">
<img src="./assets/img6.jpeg" alt="img6">
</div>
But the following code generates only 3 of the 6 images:
let album = document.getElementById("album");
let nextButton = document.getElementById('nextButton');
nextButton.addEventListener('', () => {
album.scrollBy(window.innerWidth, 0);
})
Object.keys(album.children).forEach(key => {
if (album.children[key].tagName === 'IMG') {
let newDiv = document.createElement('div');
newDiv.className = "slide";
newDiv.append(album.children[key]);
album.replaceChild(newDiv, album.children[key]);
}
})
and has an error:
Uncaught TypeError: Cannot read property 'tagName' of undefined
at Object.keys.forEach.key (index.js:9)
at Array.forEach (<anonymous>)
at index.js:8
and the generated carousel is:
Ideas? Thanks in advance.

This happens because .children creates a live collection of nodes so the iteration changes while you insert new children div inside the forEach
you could instead create a static collection like
let nodes = document.querySelectorAll('#album > img');
and iterate over that collection of nodes
let album = document.getElementById('album');
let nodes = document.querySelectorAll('#album > img');
Object.keys(nodes).forEach(i => {
let slide = document.createElement('div');
slide.className = 'slide';
slide.appendChild(nodes[i]);
album.appendChild(slide)
});
Codepen demo
The generated source is

Related

Unable to select all image elements

I'm using an intersection observer to implement lazy loading on my images. If I only select one image with the querySelector, then it works. But for some reason I can't get querySelectorAll to work. I also tried getElementsByClassName and this is still not working. Any solutions??
html:
<section class="section" id="section--2">
<div class="photo__container--two">
<img
data-src="portrait/4-vertical.JPG"
alt=""
class="fade-in img-vertical-lazy"
/>
<img
data-src="portrait/5-vertical.JPG"
alt=""
class="fade-in img-vertical-lazy"
/>
<img
data-src="portrait/6-vertical.JPG"
alt=""
class="fade-in img-vertical-lazy"
/>
</div>
</section>
JavaScript:
const lazyImages = document.querySelectorAll('.img-vertical-lazy');
const lazyLoading = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
entry.target.src = entry.target.getAttribute('data-src');
lazyLoading.unobserve(entry.target)
});
});
lazyLoading.observe(lazyImages);
According to the InstersectionObserver docs, the observe function receives a single node, not an array of nodes
You should rewrite the last line to this
lazyImages.forEach(image => lazyLoading.observe(image))

Remove last <img> inside a <div> [duplicate]

This question already has answers here:
How to remove last element from div?
(4 answers)
Closed 1 year ago.
I have <div> containing multiple <img>.
<div class="content">
<img src="img.png"/>
<img src="img.png"/>
<img src="img.png"/>
<img src="img.png"/>
</div>
The quantity <img> inside the <div> is dynamic.
I have a function that can remove all <img>. How do I modify the function to only remove last <img> inside the <div>?
document
.querySelectorAll(".content img")
.forEach((img) => img.remove());
querySelectorAll creates an array. So you just need to remove the last thing which is in the array. Something like this should work
const images = document.querySelectorAll(".content img")
images[images.length - 1].remove()
There are a number of ways to do what you are asking. Keep in mind that the return value of querySelectorAll is an array-like NodeList which can be accessed using standard array syntax, implements some array methods (forEach for example) or can be coerced to an array.
If you want to keep working with the result of querying all img elements in your div you can simply access the last element in the returned NodeList and remove it.
const images = document.querySelectorAll('.content img');
images[images.length-1].remove();
// or spread the NodeList to an array and pop
[...document.querySelectorAll('.content img')].pop().remove();
Alternatively, you can query the last img element directly using either :last-child or :last-of-type
document.querySelector('.content img:last-child').remove();
// or
document.querySelector('.content img:last-of-type').remove();
document
.getElementById('lastchild')
.addEventListener('click', () => document.querySelector('.content img:last-child').remove());
document
.getElementById('lasttype')
.addEventListener('click', () => document.querySelector('.content1 img:last-of-type').remove());
document
.getElementById('lastindex')
.addEventListener('click', () => {
imgs = document.querySelectorAll('.content2 img');
imgs[imgs.length - 1].remove()
});
document
.getElementById('pop')
.addEventListener('click', () => [...document.querySelectorAll('.content3 img')].pop().remove());
<div class="content">
<img src="https://source.unsplash.com/random/100x100/?sig=1"/>
<img src="https://source.unsplash.com/random/100x100/?sig=2"/>
<img src="https://source.unsplash.com/random/100x100/?sig=3"/>
<img src="https://source.unsplash.com/random/100x100/?sig=4"/>
</div>
<button type='button' id='lastchild'>Remove :last-child</button>
<hr>
<div class="content1">
<img src="https://source.unsplash.com/random/100x100/?sig=5"/>
<img src="https://source.unsplash.com/random/100x100/?sig=6"/>
<img src="https://source.unsplash.com/random/100x100/?sig=7"/>
<img src="https://source.unsplash.com/random/100x100/?sig=8"/>
</div>
<button type='button' id='lasttype'>Remove :last-of-type</button>
<hr>
<div class="content2">
<img src="https://source.unsplash.com/random/100x100/?sig=9"/>
<img src="https://source.unsplash.com/random/100x100/?sig=10"/>
<img src="https://source.unsplash.com/random/100x100/?sig=11"/>
<img src="https://source.unsplash.com/random/100x100/?sig=12"/>
</div>
<button type='button' id='lastindex'>Remove imgs[length-1]</button>
<hr>
<div class="content3">
<img src="https://source.unsplash.com/random/100x100/?sig=13"/>
<img src="https://source.unsplash.com/random/100x100/?sig=14"/>
<img src="https://source.unsplash.com/random/100x100/?sig=15"/>
<img src="https://source.unsplash.com/random/100x100/?sig=16"/>
</div>
<button type='button' id='pop'>Remove pop()</button>
If you want to use jQuery you can find last child and remove it.
Like this: $('.content').children('img').last().remove()
function deleteLastImg() {
$('.content').children('img').last().remove()
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<button onClick="deleteLastImg()" >Delet last image</button>
<div class="content">
<img src="img.png" />
<img src="img.png" />
<img src="img.png" />
<img src="img.png" />
<span>this is span</span>
</div>
Use .lastChild.remove();
let yourDiv = document.getElementById("yourDiv");
function yourFunction () {
yourDiv.lastChild.remove();
}
<button onclick="yourFunction()">Click me</button>
<div class="content" id="yourDiv">
<img src="https://images.ctfassets.net/hrltx12pl8hq/7yQR5uJhwEkRfjwMFJ7bUK/dc52a0913e8ff8b5c276177890eb0129/offset_comp_772626-opt.jpg?fit=fill&w=800&h=300"/>
<img src="https://images.ctfassets.net/hrltx12pl8hq/7yQR5uJhwEkRfjwMFJ7bUK/dc52a0913e8ff8b5c276177890eb0129/offset_comp_772626-opt.jpg?fit=fill&w=800&h=300"/>
<img src="https://images.ctfassets.net/hrltx12pl8hq/7yQR5uJhwEkRfjwMFJ7bUK/dc52a0913e8ff8b5c276177890eb0129/offset_comp_772626-opt.jpg?fit=fill&w=800&h=300"/>
<img src="https://images.ctfassets.net/hrltx12pl8hq/7yQR5uJhwEkRfjwMFJ7bUK/dc52a0913e8ff8b5c276177890eb0129/offset_comp_772626-opt.jpg?fit=fill&w=800&h=300"/>
</div>

Randomizing 4 div elements in a row Javascript + JQuery

I have 4 div elements in a row (they are cards). I need to mix these divs on every page reflesh. How can I mix them?
I did this:
var random = Math.floor(Math.random() * $(".card").length);
$(".card")
.hide()
.eq(random)
.show();
But it gives just 1 random div. I need 4 random divs.
Here is the divs:
<div className="card clubs" ref="card1">
<img className="img" src={a} alt="a" />
</div>
<div className="card diamonds" ref="card2">
<img className="img" src={b} alt="b" />
</div>
<div className="card hearts" ref="card3">
<img className="img" src={c} alt="c" />
</div>
<div className="card spades" ref="card4">
<img className="img" src={d} alt="d" />
</div>
You can use something like this:
var parent = $("#cards"); // Parent container containing '.card' objects
var cards = parent.children();
while (cards.length) {
parent.append(cards.splice(Math.floor(Math.random() * cards.length), 1)[0]);
}
assuming your cards are wrapped in:
<div id='cards'>
<div className="card clubs" ref="card1">
<img className="img" src={a} alt="a" />
</div>
<div className="card diamonds" ref="card2">
<img className="img" src={b} alt="b" />
</div>
<div className="card hearts" ref="card3">
<img className="img" src={c} alt="c" />
</div>
<div className="card spades" ref="card4">
<img className="img" src={d} alt="d" />
</div>
</div>
What your code does, is choosing the card that will be displayed ("randomly"). If you want to display all the four cards and just shuffle their position, you will have to reposition them randomly.
https://css-tricks.com/snippets/jquery/shuffle-children/
$.fn.shuffleChildren = function() {
$.each(this.get(), function(index, el) {
var $el = $(el);
var $find = $el.children();
$find.sort(function() {
return 0.5 - Math.random();
});
$el.empty();
$find.appendTo($el);
});
};
randojs.com can handle jQuery elements, so it makes this kinda simple.
//grab shuffled array of jQuery card elements
var shuffledCards = randoSequence($(".card"));
//store their shuffled htmls
var shuffledCardHTMLs = [];
for(var i = 0; i < shuffledCards.length; i++){
shuffledCardHTMLs[i] = shuffledCards[i].value[0].outerHTML;
}
//replace cards on page with those shuffled htmls
for(var i = 0; i < shuffledCardHTMLs.length; i++){
$(".card").eq(i)[0].outerHTML = shuffledCardHTMLs[i];
}
This solution does NOT care where on the page the card elements are located; it will handle anything. We store the htmls before we start replacing card elements on the page because we don't want to end up trying to access the html of an element that we've already overwritten. If you want to use this code, just toss this in the head tag of your HTML document first:
<script src="https://randojs.com/1.0.0.js"></script>

How to add Event Listener on Multiple tag at once

I'm new in Javascript. I have this images that I want to replace on the main-img. The code below is working fine but I just want to know how to do this by using less code in Javascript.
<div id="container">
<div id="side-img">
<img id="side1" onclick="side1()" src="img1.jpeg">
<img id="side2" onclick="side2()" src="img2.jpeg">
<img id="side3" onclick="side3()" src="img3.jpeg">
<img id="side4" onclick="side4()" src="img4.jpeg">
</div>
<div id="main-img">
<img id="main" src="img0.jpeg">
</div>
</div>
<script type="text/javascript">
var sideimg = document.querySelectorAll('#side-img img');
var main = document.querySelector('#main');
function side1() {
main.src = sideimg[0].src;
}
function side2() {
main.src = sideimg[1].src;
}
function side3() {
main.src = sideimg[2].src;
}
function side4() {
main.src = sideimg[3].src;
}
</script>
You could simplify it by modifying the image tags like this:
<img id="side1" onclick="side(0)" src="img1.jpeg">
<img id="side2" onclick="side(1)" src="img2.jpeg">
<img id="side3" onclick="side(2)" src="img3.jpeg">
<img id="side4" onclick="side(3)" src="img4.jpeg">
And remove all side() functions in your JS script and add this function:
var sideimg = document.querySelectorAll('#side-img img');
var main = document.querySelector('#main');
function side(index) {
main.src = sideimg[index].src;
}
You can programatically attach an onclick handler for each image.
document.querySelectorAll('img').forEach((img) => {
img.onclick = () => {
console.log(img.src)
document.querySelector('#main').src = img.src
}
})
<div id="container">
<div id="side-img">
<img id="side1" src="img1.jpeg">
<img id="side2" src="img2.jpeg">
<img id="side3" src="img3.jpeg">
<img id="side4" src="img4.jpeg">
</div>
<div id="main-img">
<img id="main" src="img0.jpeg">
</div>
</div>
Delegation included version
JS
const viewer = document.querySelector('#main');
document.addEventListener('click', (event) => {
if (typeof event.target.hasAttribute('data-clickable') && event.target.src) {
viewer.src = event.target.src;
}
})
HTML
<div id="container">
<div id="side-img">
<img id="side1" data-clickable src="img1.jpeg">
<img id="side2" data-clickable src="img2.jpeg">
<img id="side3" data-clickable src="img3.jpeg">
<img id="side4" data-clickable src="img4.jpeg">
</div>
<div id="main-img">
<img id="main" src="img0.jpeg">
</div>
</div>
I'd suggest:
// retrieving the common ancestor element of the <img> elements:
let sideImage = document.querySelector('#side-img'),
// defining a named function, using arrow syntax; 'e' is a reference
// to the event object, passed automatically from the
// EventTarget.addEventListener() method:
imageChange = (e) => {
// we retrieve the element with the 'id' of 'main', and update
// its src property to be that of the clicked element;
// e.target retrieves the element upon which the event was
// originally triggered:
document.getElementById('main').src = e.target.src;
};
sideImage.addEventListener('click', imageChange);
let sideImage = document.querySelector('#side-img'),
imageChange = (e) => {
document.getElementById('main').src = e.target.src;
};
sideImage.addEventListener('click', imageChange);
<div id="container">
<div id="side-img">
<img id="side1" src="https://via.placeholder.com/100.png?text=image1">
<img id="side2" src="https://via.placeholder.com/100.png?text=image2">
<img id="side3" src="https://via.placeholder.com/100.png?text=image3">
<img id="side4" src="https://via.placeholder.com/100.png?text=image4">
</div>
<div id="main-img">
<img id="main" src="img0.jpeg">
</div>
</div>
JS Fiddle demo.
With reference to the question left in the comments, by the OP:
Can you please explain to me how the container #side-img was looping through each image and adding an event listener to them?
Sure, in this case we used event-delegation taking advantage of the way that events bubble through the DOM.
Rather than binding an event-listener to multiple <img> elements we took advantage of the way that events bubble through the DOM; this means that we listened for the click event as it reaches the #side-img, and look at the Event Object's target property to find the element upon which the event was initially triggered.
References:
Arrow functions.
document.getElementById().
document.querySelector().
Event delegation.
EventTarget.addEventListener().

How to target all classes with javascript. No jQuery

I am writing a responsive program that keeps changing the width of sertain elements, corresponding to other elements which have a % based width. Which works fine. But i ran into a problem when i wanted to change all images in side a div to be as big as the grand parent of the images.
JS:
var thebigone = document.getElementById('imgpresentation');
var demimages = document.getElementsByClassName('presentatinthis');
fixtheresponsiveness = setInterval(fixthis,1000);
function fixthis()
{
demimages.style.width = thebigone.offsetWidth+"px";
}
fixtheresponsiveness();
HTML:
<div id="imgpresentation" class="imgpresentation">
<div id="slidethemimgpresentation" class="slidethemimgpresentation">
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg1.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg2.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg3.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg4.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg5.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg6.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg7.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg8.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg9.jpg"/>
<img class="presentatinthis" src="img/billeder/xyachtvisit/xyachtbesoeg10.jpg"/>
</div>
</div>
It works if i replace "class" with "id" and "getelementsbyclassname" with "getelementbyid" but then it only works on the first img inside the div.
I do not wish to use jQuery, so please do not suggest $('.presentatinthis')
document.getElementsByClassName returns a NodeList, which means you would have to loop over the result to access and set each Node's style
var i = demimages.length;
while (i--) demimages[i].style.width = thebigone.offsetWidth+"px";

Categories