This is my folder structure
-- assets
|
- missing.jpg
and this is the function I used to render the movie div
function showMovies(movies) {
main.innerHTML = "";
movies.forEach((movie) => {
const { title, poster_path, vote_average, overview } = movie;
const movieEl = document.createElement("div");
movieEl.classList.add("movie");
movieEl.innerHTML = `
<img
src="${IMG_PATH + poster_path}"
alt="${title}"
/>
<div class="movie-info">
<h3>${title}</h3>
<span class="${getClassByRate(vote_average)}">${vote_average}</span>
</div>
<div class="overview">
<h3>Overview</h3>
${overview}
</div>
`;
main.appendChild(movieEl);
});
}
I am trying to change the alt image in this function to display an image in my assets folder instead of just the text title.
If I understand you correctly you want to add an image when there is no image found right?
ok, so there are multiple approaches to this problem, first, if you have an invalid path, use the terniary operator to src your image correctly
src=`${IMG_PATH ? IMG_PATH + poster_path : '/assets/missing.jpg'}`
But... if the image fails to load because of the server being unavailable or some 400/500 error then that's a different story, My personal approach is to make a div with the size of the image and set multiple backgrounds using css properties for instance....
background: url("/path_to_movie_poster.gif"), url("/assets/missing.jpg");
This way if the first source failed for some reason then the default image will kick in.
Make sure your default image is the last in the least otherwise it will be on top.
Related
I didn't find any proper solution inside old threads so...
I'm trying to replicate one of the feature of SplitText plugin of GSAP (here is the reference).
I'm building a simple Gatsby website to learn a bit about react.
I've a simple component that will be rendered into several instances inside index.js.
Here is the simplified code for feature.js
import React, { useState } from 'react'
import classnames from 'classnames/bind'
import styles from '../styles/modules/feature.module.scss'
let cx = classnames.bind(styles)
const Feature = props => {
const [state] = useState({
id: props.id,
subtitle: props.subtitle,
title: props.title,
description: props.description,
content: props.contentPosition,
image: props.imgPosition,
})
return (
<section className={cx('feature')}>
<div className={cx('featureContent', `${state.content}`)}>
<div className={cx('featureContentInner')}>
<div className={cx('subtitle')}>
<h5>
<span>{`${state.id}.`}</span>
{state.subtitle}
</h5>
</div>
<h1>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>Your peace of</div>
</div>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>mind in the</div>
</div>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>heart of Venice.</div>
</div>
</h1>
<p>{state.description}</p>
</div>
</div>
<div className={cx('featureImg', `${state.image}`)}>
<div className={cx('featureImgInner')}>
<img />
</div>
</div>
</section>
)
}
export default Feature
As you can see, I'm holding props inside my state and place them in my render methods.
The only thing that is hard coded is the props.title because I'm fetching a String and I need to split that String into several lines.
Since I've several instance with different props.title the length of each line should be calculated dynamically.
The structure could be replicated each time h1 > div*x > div but each time there could be a different x.
I'm asking to this board how would you tackle this or if there is a plugin or some script to make this simple instead of calculating tons of variables (getBoundingClientRect split join etc...).
Or maybe the only solution is to pay 99 bucks for a single shot on a plugin :P
Thanks!
For the sake of forthcomers:
Setup a reference:
let paragraph = useRef(null);
This will be assigned to the text we would like to spilt:
<p ref={el => (paragraph = el)}>
this is a simple paragraph that I'm trying to split
</p>
Next, we are going to setup an initial state based on how many max lines do you expect (don't worry if there will be less lines, like when resizing, they will be hidden):
const [content, setContent] = useState({
firstLine: '',
secondLine: '',
thirdLine: ''
});
Then simply execute the splitting once the component did render.
With array function you can go as deep as you want: in my example I need the content of each line. I'll hide the generated splitting from Splittext.js and inject the result of each lines into my preformatted code.
useEffect(() => {
// Split the paragraph into words and lines
const splittedText = Splitting({ target: paragraph, by: 'lines' });
// Get an Array of Lines that contains an array of Words
const lines = splittedText[0].lines.map(line =>
line.map(word => word.textContent)
);
// Get every single lines as an Array of Strings
const contents = lines.map(line => line.join(' '));
// Hide the original paragraph
paragraph.style.display = 'none';
// Change the content state
setContent({
firstLine: contents[0],
secondLine: contents[1],
thirdLine: contents[2]
});
}, []);
Then just render how many lines of how-nested-you-want and with how-many-classes-you-like divs
Is there a way to hide or remove image while loading a new one, when changing src tag value.
Example code:
<img [src]="dynamicPath">
<button (click)="changeSrc()">Change Image Src</button>
In component:
dynamicPath = 'somePath.jpg';
changeSrc(){
this.dynamicPath = 'newPath.jpg';
}
The problem with this code is that after clicking the button, old image is still showing until new image has completely loaded, which is undesired.
Is there a way to remove it or show a hint that new image is being loaded?
Note that: my case doesn't allow solution of preloading many images at once.
You can remove the image from the DOM using *ngIf as below,
<img *ngIf="dynamicPath!=''" [src]="dynamicPath">
<button (click)="changeSrc()">Change Image Src</button>
you can set the variable to empty string ' ' as below
dynamicPath = 'somePath.jpg';
changeSrc(){
this.dynamicPath ='';
this.dynamicPath = 'newPath.jpg';
}
Just hookup the load event of the image.
html
<img [src]="dynamicPath" (load)="onload()" *ngIf="loadingImg">
<button (click)="changeSrc()">Change Image Src</button>
ts
loadingImg = true;
dynamicPath = 'somePath.jpg';
changeSrc(){
this.loadingImg = true;
this.dynamicPath = 'newPath.jpg';
}
onload() {
this.loadingImg = false;
}
you can use *ngIf in the img tag.
<img *ngIf="!loading" [src]="Path">
<button (click)="changeSrc()">Change Image Src</button>
Then you can make decision in the component.
Path = 'somePath.jpg';
loading=false;
changeSrc(){
this.loading =true;
this.Path = 'newPath.jpg';
this.loading =false;
}
Im having a hard time figuring out how to preview PDFs that I'm uploading using react-dropzone. PNG and JPG are working perfectly but I would also like to be able to show the user either the pdf itself or an image of the pdf.
Here's my code:
handleImageDrop = (files) => {
const currentFile = files[0]
const reader = new FileReader()
reader.addEventListener("load", () => {
this.setState({
imgSrc: reader.result
})
}, false)
reader.readAsDataURL(currentFile)
}
render() {
const { imgSrc } = this.state;
<div>
<Dropzone
onDrop={this.handleImageDrop}
multiple={false}>
{({getRootProps, getInputProps}) => {
return (
<div
{...getRootProps()}
>
<input {...getInputProps()} />
{
<p>Try dropping some files here, or click to select files to upload.</p>
}
</div>
)
}}
</Dropzone>
{ imgSrc ? <img src={imgSrc}/> : null}
</div>
</div>
)
}
}
It cannot be as simple as showing an image in a <img> tag but you could try some external library to display pdf files. Maybe have a try with react-pdf
I used PDF.js to render the PDF files. I set up a custom preview template with a canvas element and added a function in the "init" configuration that triggers when a file has been added and uses PDF.js to draw the first page of the file on the canvas.
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>
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 ''
}
}
},