Vue js - Set alt image when image source not found - javascript

I am working on a site which has a list of products. Each product has a corresponding image. I am binding the image url to the source attribute like below.
<img :src="product.ImageUrl"/>
If the image is not found, I want to show a default image.
I do as below in cshtml razor syntax (for reference only)
onerror='this.onerror = null;this.src = "#Url.Content("~/images/photo-not-available.jpg")";'
How do I achieve the same in Vue?

You can set an onerror handler function with #error in Vue:
<img :src="" #error="aVueFunctionThatChangesTheSrc">

Since in pure js put onerror inline like this
<img :src="ImgUrl" onerror="this.src='http://example.com/default.jpg'"/>
for vue.js we can replace it
<img :src="ImgUrl" #error="$event.target.src='http://example.com/default.jpg'"/>

I found that changing the src in the #error function kicked off a horrible endless loop of updates, causing a flickering screen etc. My solution so far is:
<span v-if="props.column.field === 'avatar'">
<span v-if="props.row.avatar">
<img alt="avatar" class="round-32" :src="`${props.row.avatar}`" #error="(() => props.row.avatar = null)"/>
</span>
<span v-else>
<img alt="avatar" class="round-32" src="../../../assets/images/avatar-2.jpg"/>
</span>
</span>

I have try to using #error but it doesn't work because in my case, the image was not found, so I try this one, and its work.
<img :src="getImgUrl(filename)">
methods: {
getImgUrl(filename){
try{
return require(`#/assets/${filename}`)
}catch(_){
return require(`#/assets/default.png`)
}
},
}

I ended up with a directive to set a fallback image and prevent the loop:
image-src-fallback.js
const setSrc = (evt) => {
const el = evt.currentTarget;
el.setAttribute("src", el.fallback);
el.fallback = "";
el.removeEventListener("error", _listener);
};
const _listener = (evt) => setSrc(evt);
export default {
bind(el, binding) {
el.fallback = binding.value;
el.addEventListener("error", _listener);
},
unbind(el) {
el.removeEventListener("error", _listener);
},
};
Global import
import imageSrcFallback from "./directives/image-src-fallback.js";
Vue.directive("src-fallback", imageSrcFallback);
Use
<img
v-src-fallback="user.avatar_fallback_url"
:src="user.avatar_url"
/>

Sometimes you can create a method, for example in my case some images are not found because clients have not uploaded images, then you can create a method for it: you can return any value as an image or simple '' for any image.
<div class="flex-shrink-0">
<img
class="h-72 w-full object-cover"
:src="showFirstImageGallery(p.project_images)"
/>
</div>
methods: {
showFirstImageGallery(v){
if (v.length > 1) {
return v[1].img_url
} else {
return ''
}
}
},

Related

Changing image onclick then changing it back in js

fiddle:
https://jsfiddle.net/0r7v923u/2/
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" class="logo" alt="Banner" onclick="ratesD(this)" />
JS:
function ratesD(image) {
if (img.attr('src') == "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png") {
image.src = "https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png";
} else {
image.src = "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png"
}
}
I am simply trying to change the image back and forth on click. The function below changes it but it does not return to the previous image:
function ratesD(image) {
image.src = 'https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png';
}
I thought it only needs to change using img.attr('src') == what do I need to change for the if condition?
First you are trying to access the wrong property of the image object (attr instead of src) and the second function is not checking the current image source before changing it. To fix this, the function should check the current src of the image and change it to the other URL depending on its value. Try this.
function ratesD(image) {
if (image.src == "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png") {
image.src = "https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png";
} else {
image.src = "https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png"
}
}
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" class="logo" alt="Banner" onclick="ratesD(this)" />
It's a bad idea to force load/unload your images (even if they are in the system cache) every time you click on them.
Load them only once, and switch their display at each click.
const bannerImgs = document.querySelector('#banner-images');
bannerImgs.onclick =_=> bannerImgs.classList.toggle('seeOther');
#banner-images > img {
width : 100px;
height : 100px;
}
#banner-images.seeOther > img:first-of-type,
#banner-images:not(.seeOther) > img:last-of-type {
display : none;
}
<div id="banner-images" >
<img src="https://dirask.com/static/bucket/1631898942509-VMYrnXyYZv--image.png" alt="Banner" >
<img src="https://dirask.com/static/bucket/1633375165831-yjQ7G6WQeL--image.png" alt="Banner" >
</div>

How i can switch between icons with javascript when clicked?

How can I switch between icons with javascript when clicked?
function mode() {
var moon = document.getElementById("mode");
moon.src = "https://img.icons8.com/sf-black-filled/35/fffffe/moon-symbol.png";
moon.id = "lightmode";
moon.onclick = lightmode();
}
function lightmode() {
var sun = document.getElementById("lightmode");
sun.src = "https://img.icons8.com/sf-black-filled/34/000000/sun.png";
sun.onclick = mode();
}
body { background-color:grey }
<div class="nav-icons">
<img id="mode" class="moon" onclick="mode()" src="https://img.icons8.com/sf-black-filled/35/fffffe/moon-symbol.png">
<img id="notification" src="https://img.icons8.com/ios-glyphs/34/fffffe/notification-center.png" alt="" srcset="">
</div>
You are setting up event listeners in an incorrect manner
Here is a toggle using classes and data attributes
const srcs = {
"dark": "https://img.icons8.com/sf-black-filled/35/fffffe/moon-symbol.png",
"light": "https://img.icons8.com/sf-black-filled/34/000000/sun.png"
};
const modeToggle = document.getElementById("modeToggle");
modeToggle.addEventListener("click", function() {
let mode = this.dataset.mode;
mode = mode === "dark" ? "light" : "dark"; // toggle
this.src = srcs[mode]; // image
this.dataset.mode = mode; // save in element
document.body.classList.toggle("dark",mode==="dark"); // toggle page too
})
document.body.classList.toggle("dark",modeToggle.dataset.mode==="dark"); // initialise the page, possibly from localStorage
body {
background-color: grey
}
body.dark {
background-color: black;
}
<div class="nav-icons">
<img id="modeToggle" data-mode="light" src="https://img.icons8.com/sf-black-filled/34/000000/sun.png">
<img id="notification" src="https://img.icons8.com/ios-glyphs/34/fffffe/notification-center.png" alt="" srcset="">
</div>
this may solve your problem, please use proper names for your functions, e.g instead of mode() use changeMode() as a name
javascript:
let isChanged = false;
let mode = document.querySelector('#mode');
function changeMode(){
isChanged = !isChanged;
if(isChanged) {
mode.src = "https://img.icon8.com/sf-black-filled/35/ffffffe/moon-symbol.png"
} else {
mode.src = "https://img.icon8.com/sf-black-filled/34/000000/sun.png"
}
}
Toggling between two different images/icons/styles is very simple in Javascript. You could, for instance, create a simple Object literal with name/value pairs for both icons as per the following. A simple ternary operator determines what the src should be based upon the current src
document.querySelector('.nav-icons > img.sunmoon').addEventListener('click',function(e){
let icons={
sun:'https://img.icons8.com/sf-black-filled/34/000000/sun.png',
moon:'https://img.icons8.com/sf-black-filled/35/fffffe/moon-symbol.png'
};
this.src = this.src==icons.moon ? icons.sun : icons.moon;
});
body {
background-color: grey
}
<div class="nav-icons">
<img class="sunmoon" src="https://img.icons8.com/sf-black-filled/35/fffffe/moon-symbol.png" />
<img id="notification" src="https://img.icons8.com/ios-glyphs/34/fffffe/notification-center.png" alt="" srcset="" />
</div>

Only let HTML continue once all images have loaded

I recycled a lot of code from an old JS project I made back in the very beginning of my learning process, and back then I knew nothing about DOM events. Specifically in this case, onload. What I am looking for is someway to only let the website begin to run once all images have loaded, without putting everything into one big onload function, or rewriting all my code. Is there anyway to do this?
<img src="placeholder.png" onload="continue()" width="100" height="100">
<script>
function continue() {
//This is where I am stuck
}
</script>
The simplest solution would be to set the display property of the body to none when the page is loading, and then have continue() make it visible.
CSS:
body {
display: none;
}
JS:
function continue() {
document.body.style.display = "";
}
If the images aren't going to be dynamically added to DOM you can simply just do the followingaIf the images aren't going to be dynamically added to DOM you can simply just do the following
<html>
<body style="display: none;">
<img src="https://via.placeholder.com/100" alt="">
<img src="https://via.placeholder.com/200" alt="">
<img src="https://via.placeholder.com/300" alt="">
<script>
const imageCollection = Array.from(document.getElementsByTagName('img'));
const promisifier = (imageNode) => {
return new Promise((resolve, reject) => {
imageNode.addEventListener("load", () => {
console.log('image loaded', imageNode);
resolve('Loaded')
});
})
}
Promise.all(imageCollection.map(img => promisifier(img))).then((resp)=>{
document.body.style.display = 'block';
console.log('All images completed Loading');
})
</script>
</body>
</html>
If images are going to be dynamically added you could go for Mutation Observer, let me know if thats the case will add that too.

JavaScript / Jquery exchange img png source with gif

I am trying to Exchange the src of an Image tag on a click Event, the Image src is a png file, but if I click on the Image the source shall Change to a give file.
<img src="images/eye.png" alt="Logo"
id="sidebar-collapse img-logo-main-page"
width="80">
And this is the JavaScript, I get the console Output in the console but Nothing changes:
const gifEgg = {
elements: {
logo: $('#img-logo-main-page'),
logoOverlay: $('#sidebar-image-collapse')
},
addGif () {
this.elements.logoOverlay.click(() => {
console.log("clicked");
this.elements.logo.attr('src', '../images/eyes_move.gif');
});
}
};
gifEgg.addGif();
The problem is that you are treating an id like a class. You gave the image an id of sidebar-collapse img-logo-main-page, but are trying to refer to it with #img-logo-main-page. An element can only have one id, but can have many classes. If you gave it a class name of sidebar-collapse img-logo-main-page, you could refer to it with .img-logo-main-page (any one or more of its classes), but an id must be unique and each element can only have one id.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="https://images.pexels.com/photos/658687/pexels-photo-658687.jpeg?auto=compress&cs=tinysrgb&h=350" alt="Logo"
id="img-logo-main-page"
width="80">
<button id="sidebar-image-collapse">
Change Image Source
</button>
<script>
const gifEgg = {
elements: {
logo: $('#img-logo-main-page'),
logoOverlay: $('#sidebar-image-collapse')
},
addGif () {
this.elements.logoOverlay.click(() => {
console.log("clicked");
this.elements.logo.prop('src', 'http://2.bp.blogspot.com/-3jbHdEj7o2k/Uk6zNIfJkqI/AAAAAAAAB5s/zf7UzbSkp80/s200/zrikh+ajig.gif');
});
}
};
gifEgg.addGif();
</script>

Execute code after rendering all images in vuejs

I want to do some code execution after all images are loaded (need the set scroll in a specific position). I use nextTik() but Codes are processed before loading images . We also can't use mounted and created and methods like this because codes should be executed after clicking on a button.
is there any method or trick?
thanks
You can use the load event on each image to check if all images have been loaded.
example: https://jsfiddle.net/jacobgoh101/e5bd86k6/1/
<div id="app">
<img #load="handleLoad" #error="handleLoad" v-for="i in 10" :src="`https://picsum.photos/${Math.random()*300}/700/?random`"/>
</div>
Javascript
new Vue({
el: "#app",
data: {
imgLoaded: 0
},
methods: {
handleLoad: function(todo){
this.imgLoaded++;
if(this.imgLoaded === 10) {
alert('all image loaded')
}
}
}
})
Based on ( https://stackoverflow.com/a/50481269 enter link description here) answer I did something like this:
<img class="card-img-top" :src=" 'images/' + (data.src || 'image-default.png') "
:alt="data.alt" #load="handleImagesLoad" #error="handleImagesLoad">
(#load and #error on each image on my page) and
handleImagesLoad: function($event){
var images = $('img:not([loaded])');
var target = $($event.target);
$(target).attr('loaded', true);
if(images.length <= 1){
$('#preloader').fadeOut();
}
},
Here is my approach for a component having both "static" images and an an arbitray number of images fetched from database :
Append two items to the component data: 'imgsLength' and 'loadedImgsLength'
Append a 'load' event handler to every img element, that increments 'loadedImgsLength'
After all fetched data are loaded, set 'imgsLength' to the number of all img elements
So, you know that all images are loaded when 'loadedImgsLength' > 0 and 'loadedImgsLength' == 'imgsLength'
In this exemple, my component won't be visible until all images are loaded:
<template>
<div v-show="ready" ref="myComponent">
...
<img src="..." #load="loadedImgsLength++" /> (static image)
...
<img v-for="img in fetchedImg" #load="loadedImgsLength++" /> (dynamic images)
</div>
</template>
<script>
...
data(){
return {
...,
imgsLength : 0,
loadedImgsLength: 0,
...,
}
},
computed: {
ready(){
return (this.loadedImgsLength > 0 && this.loadedImgsLength == this.imgsLength)
}
},
async created(){
... fetch all data
this.$nextTick(() => this.$nextTick(this.imgsLength = this.$refs.myComponent.getElementsByTagName("img").length))
}

Categories

Resources