Hide a section if the images inside are not present - javascript

I have an HTML code with more section like this:
<section class="grid-1">
<div class="item-2">
<div class="item-6">
<img src="../img/image1.png" onclick="openModal();currentSlide(4)" class="hover-shadow cursor" style="display: none;">
</div>
<div class="item-6">
<img id="currentPhoto" src="../img/image2.png" onclick="openModal();currentSlide(5)" class="hover-shadow cursor" style="display: none;">
</div>
<div class="item-6">
<img id="currentPhoto" src="../img/image3.png" onclick="openModal();currentSlide(6)" class="hover-shadow cursor" style="display: none;">
</div>
</div>
</section>
the style="display: none;" has been added from this code:
document.addEventListener("DOMContentLoaded", function(event) {
document.querySelectorAll('img').forEach(function(img){
img.onerror = function(){this.style.display='none';};
})
});
but is it possible to hide all the section class grid-1 if all the images sources are not available?

You could add a class to the not loaded images (or a data-attribute if you may) and then compare they to the total images amount in the grid.
function checkGrid(image) {
const grid = image.closest('section');
const gridImages = grid.querySelectorAll('img');
const gridImagesWithError = grid.querySelectorAll('img.invalid-src');
if(gridImagesWithError.length === gridImages.length) {
grid.style.display = 'none';
}
}
document.addEventListener("DOMContentLoaded", function(event) {
const images = document.querySelectorAll('img');
images.forEach(function(image) {
image.onerror = function() {
this.classList.add('invalid-src');
this.style.display = 'none';
checkGrid(this);
};
});
});
Although this works, in order to check multiple grids, it is recommended that you add a class to the grids so the query selector wouldn't have to rely solely on the section tag which can be unsafe.

Related

Toggle between multiple images

I have 4 images black, red, orange and green - and the disabled version in gray.
What I want to achieve is that when I click on a disabled one, the other ones should turn gray/disabled and the one clicked should turn to his color image.
I tried following now this will get his child div and will do the toggle trick with the clicked image, but it won't disable the other ones. How can I disable all the other ones?
$('.toggle').each(function() {
var clickCounter = 0,
firstEdit = false;
$(this).on('click', function() {
clickCounter++;
if (clickCounter == 1 || firstEdit == true) {
$(this).toggle();
$(this).next('.toggled').toggle();
clickCounter = 0;
firstEdit = true;
}
});
});
$('.toggled').on('click', function() {
$(this).toggle();
$(this).prev('.toggle').toggle();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="toggle"><img class="" src="data/img/controller/orange.jpg"></div>
<div class="toggled"><img class="" src="data/img/controller/orange_disabled.jpg"></div>
<div class="toggle"><img class="" src="data/img/controller/red.jpg"></div>
<div class="toggled"><img class="" src="data/img/controller/red_disabled.jpg"></div>
<div class="toggle"><img class="" src="data/img/controller/black.jpg"></div>
<div class="toggled"><img class="" src="data/img/controller/black_disabled.jpg"></div>
<div class="toggle"><img class="" src="data/img/controller/green.jpg"></div>
<div class="toggled"><img class="" src="data/img/controller/green_disabled.jpg"></div>
You'll need loop through all images in onclick event handler and set proper class on each image. Also note, you could make images look grey with simple css filter, don't need create separate images
const test = document.getElementById("test");
test.addEventListener("click", e =>
{
for(let i = 0, children = test.children; i < children.length; i++)
{
children[i].classList.toggle("selected", children[i] === e.target);
}
});
.content > img:not(.selected)
{
filter: saturate(0);
opacity: 0.5;
}
<span id="test" class="content">
<img src="https://lh3.googleusercontent.com/IlhDxQVsR17dwwER5xYZJej867KrdSx0K5eyRP2RFP4eQJMD2pi0ZGBhrMOcajBUP9M54lpmIr90JecPUFGPaRe3sDZ82RvHBSw1rw-YJvQs7J8K3g=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/taykG37GWDgY-FGkdogDvsHSJMUGRMvkuVRT6yR-5UNkKvGRKeRlpGYXlslocOcS0txlfUdGW59JGtzADknxbMqnh6AtVCv9EXyB8nHp80YsRNA0Yw=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/aS2Up3osDMLTua1vXPTqnXko13KbIAmB0nQ44AP_IFTEt-VjUa6Tz2MC9jdH11bsZfjdiR8z4HbnxvhmmxSU1swKrtjc5PXreP6i=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/dTH5_J82_TqXaLW_Hzq1rbMVqfzRWG9PkcKgHdjXphAy9M4MZF5Q7_cQZeM1kbqEYMysrBLlY4szACDZwIbP7Jm17BnGNjT0Tht8Qw=w102-h68-n-l50-sg-rj">
</span>
P.S.
Please don't use bloatware jquery, save the planet.
Delegation for dynamic content
jQuery version
Change toggleClass to addClass to not toggle the clicked image
$('#container').on('click', 'img', function() {
$(this).siblings().each(function() { $(this).removeClass('selected') });
$(this).toggleClass('selected')
});
.content>img:not(.selected) {
filter: saturate(0);
opacity: 0.5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div class="content">
<img src="https://lh3.googleusercontent.com/IlhDxQVsR17dwwER5xYZJej867KrdSx0K5eyRP2RFP4eQJMD2pi0ZGBhrMOcajBUP9M54lpmIr90JecPUFGPaRe3sDZ82RvHBSw1rw-YJvQs7J8K3g=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/taykG37GWDgY-FGkdogDvsHSJMUGRMvkuVRT6yR-5UNkKvGRKeRlpGYXlslocOcS0txlfUdGW59JGtzADknxbMqnh6AtVCv9EXyB8nHp80YsRNA0Yw=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/aS2Up3osDMLTua1vXPTqnXko13KbIAmB0nQ44AP_IFTEt-VjUa6Tz2MC9jdH11bsZfjdiR8z4HbnxvhmmxSU1swKrtjc5PXreP6i=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/dTH5_J82_TqXaLW_Hzq1rbMVqfzRWG9PkcKgHdjXphAy9M4MZF5Q7_cQZeM1kbqEYMysrBLlY4szACDZwIbP7Jm17BnGNjT0Tht8Qw=w102-h68-n-l50-sg-rj">
</div>
<div class="content">
<img src="https://lh3.googleusercontent.com/IlhDxQVsR17dwwER5xYZJej867KrdSx0K5eyRP2RFP4eQJMD2pi0ZGBhrMOcajBUP9M54lpmIr90JecPUFGPaRe3sDZ82RvHBSw1rw-YJvQs7J8K3g=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/taykG37GWDgY-FGkdogDvsHSJMUGRMvkuVRT6yR-5UNkKvGRKeRlpGYXlslocOcS0txlfUdGW59JGtzADknxbMqnh6AtVCv9EXyB8nHp80YsRNA0Yw=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/aS2Up3osDMLTua1vXPTqnXko13KbIAmB0nQ44AP_IFTEt-VjUa6Tz2MC9jdH11bsZfjdiR8z4HbnxvhmmxSU1swKrtjc5PXreP6i=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/dTH5_J82_TqXaLW_Hzq1rbMVqfzRWG9PkcKgHdjXphAy9M4MZF5Q7_cQZeM1kbqEYMysrBLlY4szACDZwIbP7Jm17BnGNjT0Tht8Qw=w102-h68-n-l50-sg-rj">
</div>
</div>
Vanilla - it does not toggle the clicked image
const container = document.getElementById('container');
container.addEventListener('click', e => {
const tgt = e.target;
if (!tgt.matches('img')) return;
const images = tgt.closest('.content').querySelectorAll('img');
images.forEach(img => img.classList.toggle('selected', img === tgt));
});
.content>img:not(.selected) {
filter: saturate(0);
opacity: 0.5;
}
<div id="container">
<div class="content">
<img src="https://lh3.googleusercontent.com/IlhDxQVsR17dwwER5xYZJej867KrdSx0K5eyRP2RFP4eQJMD2pi0ZGBhrMOcajBUP9M54lpmIr90JecPUFGPaRe3sDZ82RvHBSw1rw-YJvQs7J8K3g=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/taykG37GWDgY-FGkdogDvsHSJMUGRMvkuVRT6yR-5UNkKvGRKeRlpGYXlslocOcS0txlfUdGW59JGtzADknxbMqnh6AtVCv9EXyB8nHp80YsRNA0Yw=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/aS2Up3osDMLTua1vXPTqnXko13KbIAmB0nQ44AP_IFTEt-VjUa6Tz2MC9jdH11bsZfjdiR8z4HbnxvhmmxSU1swKrtjc5PXreP6i=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/dTH5_J82_TqXaLW_Hzq1rbMVqfzRWG9PkcKgHdjXphAy9M4MZF5Q7_cQZeM1kbqEYMysrBLlY4szACDZwIbP7Jm17BnGNjT0Tht8Qw=w102-h68-n-l50-sg-rj">
</div>
<div class="content">
<img src="https://lh3.googleusercontent.com/IlhDxQVsR17dwwER5xYZJej867KrdSx0K5eyRP2RFP4eQJMD2pi0ZGBhrMOcajBUP9M54lpmIr90JecPUFGPaRe3sDZ82RvHBSw1rw-YJvQs7J8K3g=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/taykG37GWDgY-FGkdogDvsHSJMUGRMvkuVRT6yR-5UNkKvGRKeRlpGYXlslocOcS0txlfUdGW59JGtzADknxbMqnh6AtVCv9EXyB8nHp80YsRNA0Yw=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/aS2Up3osDMLTua1vXPTqnXko13KbIAmB0nQ44AP_IFTEt-VjUa6Tz2MC9jdH11bsZfjdiR8z4HbnxvhmmxSU1swKrtjc5PXreP6i=w102-h68-n-l50-sg-rj">
<img src="https://lh3.googleusercontent.com/dTH5_J82_TqXaLW_Hzq1rbMVqfzRWG9PkcKgHdjXphAy9M4MZF5Q7_cQZeM1kbqEYMysrBLlY4szACDZwIbP7Jm17BnGNjT0Tht8Qw=w102-h68-n-l50-sg-rj">
</div>
</div>

How to display attached images(uploaded via lightning-formatted-rich-text) in a modal

im trying to display images that are uploaded into a modal shown below:
<template iterator:thefield={images}>
<template if:true={imagesAttached}>
<div class="slds-file slds-file_card slds-float_right" key={thefield.value.apiName} style="width: 15rem">
<div class="slds-grid slds-gutters">
<div class="slds-col slds-p-horizontal_medium">
<span>
<div class="slds-file slds-file_card slds-float_right" style="width: 15rem">
<figure>
<img src={images} alt="Description of the image" />
</figure>
</div>
</span>
</div>
</div>
</div>
</template>
</template>
I have a function that takes the image and put it in an array.
getImages(string) {
const imgRex = /<img .*?>/g // /<img [^>]*src=['"]([^'"]+)[^>]*>/gi
const images = [];
let img;
while ((img = imgRex.exec(string))) {
images.push(img);
if(images.length > 0){
this.imagesAttached = true;
}
}
return images;
}
I would appreciate the help.
Thank you.

How to randomly re-render the DOM nodes' order of precedence from a node-list?

I'm trying to shuffle cards by reordering the index of the nodes in my NodeList.
How can I achieve removing and appending the children of a class attribute I have?
HTML:
<div class="card" data-card="1" onclick="cardClicked(this)">
<img src="img/cards/1.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
<div class="card" data-card="7" onclick="cardClicked(this)">
<img src="img/cards/7.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
<img src="img/cards/1.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
JavaScript:
function shuffleCards() {
let cards = document.querySelectorAll('.card');
let cardsArray = Array.from(cards);
// reorder the nodes of the nodelist (cards)
}
There are several ways you can go about "shuffling" an array. I chose the Fisher-Yates method in an experimental "war" card game.
https://github.com/scottm85/war/blob/master/src/Game/Deck.js#L80
shuffle()
{
/* Use a Fisher-Yates shuffle...If provides a much more reliable shuffle than using variations of a sort method https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle */
let cards = this.cards;
for (let i = cards.length - 1; i > 0; i--)
{
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
[cards[i], cards[j]] = [cards[j], cards[i]];
}
this.cards = cards;
console.log("----------------- Deck Shuffled -----------------");
}
https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Granted my provided example is slightly different than your needs as I was doing this in react, had a Deck array built out, and wasn't using JS for direct DOM manipulation. In theory though, you could modify this to work with your method. Something like this:
function shuffleCards
{
let cards = document.querySelectorAll('.card');
let cardsArray = Array.from(cards);
for (let i = cardsArray.length - 1; i > 0; i--)
{
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
[cardsArray[i], cardsArray[j]] = [cardsArray[j], cardsArray[i]];
cards[i].remove();
}
cardsArray.forEach(t => document.body.appendChild(t));
}
You can use sort and Math.random() methods like this:
function shuffleCards() {
var parent = document.getElementById("parent");
let cards = document.querySelectorAll('.card');
let cardsArray = Array.from(cards);
//console.log(cardsArray)
cardsArray.sort(() => Math.random() - 0.5);
//console.log(cardsArray)
cardsArray.forEach(t => parent.appendChild(t));
// reorder the nodes of the nodelist (cards)
}
<h3> Click on card to Shuffle at the same postion</h3>
<div id="parent">
<div class="card" data-card="1" onclick="shuffleCards(this)">
card 1
<img src="img/cards/1.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
<div class="card" data-card="7" onclick="shuffleCards(this)">
card 2
<img src="img/cards/7.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
<div class="card" data-card="1" onclick="shuffleCards(this)">
card 3
<img src="img/cards/1.png" alt="">
<img class="back" src="img/cards/back.png" alt="">
</div>
</div>
Others have provided algorithms for the randomization portion, but here is how you can handle the detaching and reattaching portion. removeChild() will get you the node detached from the DOM and appendChild() will allow you to add it. Just be careful with attaching and detaching. Events can get a bit messy especially if you end up cloning the node somewhere down the line rather than just attaching it again.
(function shuffleCards() {
let container = document.querySelector('#container');
let cards = container.querySelectorAll('.card');
[...cards].map(
node => container.removeChild(node)
).sort(
() => Math.random() - 0.5 // shamelessly stolen from Alireza Ahmadi
).forEach(
node => container.appendChild(node)
);
})()
<div id="container">
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 1
</div>
<div class="card" data-card="7" onclick="cardClicked(this)">
Card 2
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 3
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 4
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 5
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 6
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 7
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 8
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 9
</div>
<div class="card" data-card="1" onclick="cardClicked(this)">
Card 10
</div>
</div>
The following approach is agnostic to where the queried nodes need to be removed from, and after shuffling, have to be inserted into again.
Thus one does not need to know anything about the DOM structure except for the element query.
Basically ...
Use only reliable/proven shuffle functions like _.shuffle.
Create an array from the queried node list.
Map it with node items in a way that each (child) node gets removed from the DOM but preserves not only its own reference but also the reference of its parent node.
Shuffle the mapped array.
Iterate the shuffled items in a way where from each item one can restore the former parent-node child-node relationship ...
function shuffleArray(arr) {
let idx;
let count = arr.length;
while (--count) {
idx = Math.floor(Math.random() * (count + 1));
[arr[idx], arr[count]] = [arr[count], arr[idx]];
}
return arr;
}
function shuffleCards() {
const removedNodeDataList = Array
.from(document.querySelectorAll('.card'))
.map(elmNode => ({
parentNode: elmNode.parentNode,
elementNode: elmNode.parentNode.removeChild(elmNode),
}));
shuffleArray(removedNodeDataList)
.forEach(({ parentNode, elementNode }) =>
parentNode.appendChild(elementNode)
);
}
function init() {
document
.querySelector('button')
.addEventListener('click', shuffleCards);
}
init();
img { background-color: #eee; }
button { display: block; margin: 0 0 10px 0; }
<button>Shuffle Cards</button>
<div class="card" data-card="1">
<img src="https://picsum.photos/140/50?grayscale" alt="">
<img class="back" src="https://picsum.photos/120/50?grayscale" alt="">
</div>
<div class="card" data-card="2">
<img src="https://picsum.photos/100/50?grayscale" alt="">
<img class="back" src="https://picsum.photos/160/50?grayscale" alt="">
</div>
<div class="card" data-card="3">
<img src="https://picsum.photos/180/50?grayscale" alt="">
<img class="back" src="https://picsum.photos/80/50?grayscale" alt="">
</div>

IntersactionObserver() only observes the first element of a row instead of all

I'm experimenting with IntersectionObserver(), and it's behaving weirdly: I'm using a very simple layout, just 8 images in the same div with flexbox, using wrap, so basically the 8 images are positioning themself in different rows, according to the size of the viewport. I'm applying a filter class (which adds a blur filter) to each element first and then removing it when they are displayed on the screen:
HTML:
<div class="flex--cont">
<div class="box box-2">
<img src="img/1.jpg" alt="" class="img img-1">
</div>
<div class="box box-1">
<img src="img/2.jpg" alt="" class="img img-2">
</div>
<div class="box box-3">
<img src="img/3.jpg" alt="" class="img img-3">
</div>
<div class="box box-4">
<img src="img/4.jpg" alt="" class="img img-4">
</div>
<div class="box box-5">
<img src="img/5.jpg" alt="" class="img img-5">
</div>
<div class="box box-6">
<img src="img/6.jpg" alt="" class="img img-6">
</div>
<div class="box box-7">
<img src="img/7.jpg" alt="" class="img img-7">
</div>
<div class="box box-8">
<img src="img/8.jpg" alt="" class="img img-8">
</div>
</div>
JAVASCRIPT
const allImage = Array.from(document.querySelectorAll(".img"));
allImage.forEach((img) => img.classList.add("filter"));
const removeFilter = function (entries, observer) {
const [entry] = entries;
const image = entry.target;
image.classList.remove("filter");
};
const ImageObserver = new IntersectionObserver(removeFilter, {
root: null,
threshold: 0.15,
});
allImage.forEach((img) => ImageObserver.observe(img));
The thing is that the observer actually only observes the very first element of each row, so if I have 2 rows, it only gets the 1st and the 5th image, if I have 3 rows it gets the 1rst, the 4th and the 7th image and so on. I do have applied it to all of the images. Why is it doing that? Thanks for your answers!
Only the first one was changing because that was all you were targeting in the change color function by destructuring only the first array element:
const [entry] = entries;
However, the InteractionObserver callback is not called per entry but for all entries that are triggered simultaneously; hence the entries array contains all of the items being observed and you need to check the isIntersecting property like this:
const changeColor = function(entries) {
entries.forEach(entry => {
if(entry.isIntersecting) {
entry.target.style.background = 'blue';
} else {
entry.target.style.background = 'red';
}
})
}
From the MDN docs
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};

How can I get the src attribute of a child img element while looping through an array of divs?

I want to loop through some <div> tags and get the src attribute of the child <img> element but I am not sure how to do it. This is what I have so far.
const imgs = document.querySelectorAll(".imgs .imagecont");
imgClick = (event) => {
console.log(event.target.src);
};
imgs.forEach(img => img.addEventListener("click", imgClick));
<div class="imgs">
<div class="imagecont">
<img src="assets/img1.jpeg">
</div>
<div class="imagecont">
<img src="assets/img2.jpeg">
</div>
</div>
Also, I have to use vanilla javascript.
Try with Element.getAttribute()
The getAttribute() method of the Element interface returns the value of a specified attribute on the element.
const imgs = document.querySelectorAll(".imgs .imagecont");
imgClick = (event) => {
console.log(event.target.getAttribute('src'));
};
imgs.forEach(img => img.addEventListener("click", imgClick));
<div class="imgs">
<div class="imagecont">
<img src="assets/img1.jpeg">
</div>
<div class="imagecont">
<img src="assets/img2.jpeg">
</div>
</div>
Try this using getAttribute('src')
const imgs = document.querySelectorAll(".imgs .imagecont");
imgClick = (event) => {
console.log(event.target.getAttribute('src'));
};
imgs.forEach(img => img.addEventListener("click", imgClick));
<div class="imgs">
<div class="imagecont">
<img src="assets/img1.jpeg">
</div>
<div class="imagecont">
<img src="assets/img2.jpeg">
</div>
</div>

Categories