Puppeteer: How get img src inside nested selector? - javascript

I have a structure like this:
<div class="loaded active">
<img class="full" src="img1.jpg">
</div>
<div class="loaded">
<img class="full" src="img2.jpg">
</div>
<div class="loaded">
<img class="full" src="img3.jpg">
</div>
I need get link of img1.jpg.
I can get links for class="full":
const slkImg = '.full';
const imgs = await page.$$eval(slkImg, postLinks => postLinks.map(link => link.src));
but I don't know how do it for only img1.jpg.

As discussed in the comments,
page.$eval('.loaded.active .full', el => el.src)
works thanks to two things:
Using $eval rather than $$eval to select one element
Using .loaded.active .full to select the first .full descendent of the first .loading.active parent.

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>

How do I get the value of the first child element in Javascript?

I'm trying to get the value of the first child element from my HTML to show whenever I click on an image aka my 'services-cell" container, but it keeps saying the value is undefined.
<div class="services-cell">
<img class="services-cell_img" src="gallery/img-1.jpg" alt="">
<div class="services-cell_text">Digital Marketing</div>
</div>
Here is my Javascript
let galleryImages = document.querySelectorAll('.services-cell')
if(galleryImages) {
galleryImages.forEach(function(image){
image.onclick = function() {
console.log(galleryImages.firstElementChild.value)
}
})
}
I tried to add the img class as a variable as well, but it also says undefined. I want the console.log to print
<img class="services-cell_img" src="gallery/img-1.jpg" alt="">
I have multiple images with the same html except it just say img-2, img-3 etc. So ideally whenever I click on the other images it would print the same HTML value but just will say the image number that I clicked on
You created the array as galleryImages, but then rather than accessing the firstElementChild of the div, you're trying to access that property on the array. You need to do image.firstElementChild instead. Also, as far as I know, accessing .value of an image has no meaning, I think you meant to just do firstElementChild instead:
let galleryImages = document.querySelectorAll('.services-cell');
if (galleryImages) {
galleryImages.forEach(function (image) {
image.onclick = function() {
console.log(image.firstElementChild);
};
});
}
<div class="services-cell">
<img class="services-cell_img" src="https://s3.amazonaws.com/pix.iemoji.com/images/emoji/apple/ios-12/256/deciduous-tree.png" alt="">
<div class="services-cell_text">Digital Marketing</div>
</div>
<div class="services-cell">
<img class="services-cell_img" src="https://cdn-cloudfront.cfauthx.com/binaries/content/gallery/gg-en-us/icons/gg-tree-icon.png" alt="">
<div class="services-cell_text">Digital Marketing</div>
</div>
What you could do to achieve this is passing the event attribute as a parameter of the onclick function.
The event attribute has a target; which is the item that triggered the event. So for example:
if(galleryImages) {
galleryImages.forEach(function(image){
image.onclick = function(e) {
console.log(e.target.firstElementChild.value)
}
})
}
However instead of adding an eventListener to every element, it might be better to add one event handler on the parent - and check which item is clicked.
E.g.
<div class="parent">
<div class="services-cell">
<img class="services-cell_img" src="gallery/img-1.jpg" alt="" />
<div class="services-cell_text">Digital Marketing</div>
</div>
<div class="services-cell">
<img class="services-cell_img" src="gallery/img-2.jpg" alt="" />
<div class="services-cell_text">Digital Marketing</div>
</div>
</div>
And the javascript:
document.querySelector('.parent').addEventListener('click', function(e){
if(e.target.matches('.services-cell'){
// Do something with the e.target - which is the .services.cell
}
}

JS Dom manipulation issue

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

Mouse / Hover over image change parent image src

I have the following problem, lets say this is my html
<ul classname='products'>
<li classname='product'>
<div classname='product_title'>Product 1</div>
<div classname='product_thumbnail'><img src="product1.jpg"></div>
<div classname='product_images'>
<img src="product1_image1.jpg">
<img src="product1_image2.jpg">
</div>
</li>
<li classname='product'>
<div classname='product_title'>Product 2</div>
<div classname='product_thumbnail'><img src="product2.jpg"></div>
<div classname='product_images'>
<img src="product2_image1.jpg">
<img src="product2_image2.jpg">
</div>
</li>
<li classname='product'>
<div classname='product_title'>Product 3</div>
<div classname='product_thumbnail'><img src="product2.jpg"></div>
<div classname='product_images'>
<img src="product3_image1.jpg">
<img src="product3_image2.jpg">
</div>
</li>
</ul>
When I mouse over a product_image I want to change the product_thumbnail src to the product_image src. Its not very hard when I had one list item with the product_thumbnail having an ID
Then I could have done this
var $mainImage = $('#product_thumbnail'),
originalImageSrc = $mainImage.attr('src');
$('.product_images img')
.on('mouseover', function() {
var newImageSrc = $(this).attr('src');
$mainImage.attr('src', newImageSrc);
})
.on('mouseout', function() {
$mainImage.attr('src', originalImageSrc);
});
Working example: JSFiddle
Sadly enough I dont have one list item with an ID.
Of course when I try this code with classes and the html above it will always give me the src back of the first image in the first product_thumbnail div of the first list item.
I hope you guys understand my problem and someone can help me with the classes version of the jQuery example code.
Thanks in advance
First the html needs fixing - replace className with class. ClassName is the JavaScript attribute name for HTML class. For HTML jsut use class='foo'.
<ul class='products'>
<li class='product'>
<div class='product_title'>Product 1</div>
<div class='product_thumbnail'><img src="product1.jpg"/></div>
<div class='product_images'>
<img src="product1_image1.jpg">
<img src="product1_image2.jpg">
</div>
</li>
<li class='product'>
<div class='product_title'>Product 2</div>
<div class='product_thumbnail'><img src="product2.jpg"/></div>
<div class='product_images'>
<img src="product2_image1.jpg">
<img src="product2_image2.jpg">
<img src="product2_image3.jpg">
</div>
</li>
</ul>
And the JS now looks like this:
$('.product_images img')
.on('mouseover', function() {
var tgt = $(this).closest(".product").find(".product_thumbnail img")
tgt.data("orig_src", tgt.prop("src"))
tgt.prop("src", $(this).prop("src"));
})
.on('mouseout', function() {
var tgt = $(this).closest(".product").find(".product_thumbnail img")
tgt.prop("src", tgt.data("orig_src"));
});
The magic is in the selector. closest looks up the DOM from the starting element. We look for the closest element with class=product. When we find it we look for its child with class=product_thumbnail and then the child image of that. This all assumes there is only one occurrence, this code would be different if there were multiple hits.
Having found the target we first store its original src value in its data collection, then we replace the src value with the triggering img src.
On mouse out we take the src we stored in the thumbnails data and replace it.

Categories