I have an app with the Vue CLI. When the app loads, I have a bunch of images that appear with a transition when a user clicks a button. The problem is that when the user clicks a button, the corresponding image only then starts to load, meaning that most of the animation is done until then. This makes the experience quite choppy because the images suddenly pop in during the transition, displacing other elements. I want to prefetch them when the site loads.
This answer suggests using the Image class. However, according to the Vue CLI docs, Vue internally uses its own plugin for that, preload-webpack-plugin, and it apparently can be configured.
I tried to configure it so that it preloads images:
vue.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('#vue/preload-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: 'prefetch',
as (entry) {
if (/\.css$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.png$/.test(entry)) return 'image';
return 'script';
}
})
]
}
}
This only manages to screw up the final index.html, leaving it without the build scripts and styles inside.
If I remove this line:
new HtmlWebpackPlugin(),
The site still loads but the images are not prefetched. It's the same as if I never did anything in the vue.config.js file.
How do I set it up correctly?
Edit: In Vue components, I use require() to load the images, meaning they pass through Webpack. For example:
<img :src="require('../assets/img/' + img)" draggable="false">
Edit: I was able to prefetch the images as Roy J suggested in the comments:
PreloadImages.vue in my main component:
<template>
<div style="display: none;">
<img :src="require('../assets/img/foo.png')">
<img :src="require('../assets/img/bar.png')">
<img :src="require('../assets/img/baz.png')">
</div>
</template>
However, that's not the answer to my actual question - it doesn't use resource hints via <link> tags. It also requires more effort and I believe it's a bad practice.
Since the plugin is already included by VueJS I think you have to modify it with chainWebpack.
According to the preload webpack plugin documentation, you should also set include option to 'allAssets' value.
It is very common in Webpack to use loaders such as file-loader to
generate assets for specific types, such as fonts or images. If you
wish to preload these files as well, you can use include with value
allAssets
So the configuration will be something like this:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('preload').tap(options => {
options[0].as = (entry) => {
if (/\.css$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.png$/.test(entry)) return 'image';
return 'script';
}
options[0].include = 'allAssets'
// options[0].fileWhitelist: [/\.files/, /\.to/, /\.include/]
// options[0].fileBlacklist: [/\.files/, /\.to/, /\.exclude/]
return options
})
}
}
With a fresh Vue-cli installation I got the following HTML generated
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=viewport content="width=device-width,initial-scale=1">
<link rel=icon href=/favicon.ico> <title>vue-preload-images</title>
<link href=/css/app.0b9b292a.css rel=preload as=style>
<!-- Here is the logo image -->
<link href=/img/logo.82b9c7a5.png rel=preload as=image>
<link href=/js/app.30d3ed79.js rel=preload as=script>
<link href=/js/chunk-vendors.3d4cd4b5.js rel=preload as=script>
<link href=/css/app.0b9b292a.css rel=stylesheet>
</head>
<body><noscript><strong>We're sorry but vue-preload-images doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
<div id=app></div>
<script src=/js/chunk-vendors.3d4cd4b5.js> </script> <script src=/js/app.30d3ed79.js> </script> </body> </html>
I hope it will work for you.
Solution 1.
When the user clicks the button you render your image in a non-visible state.
On image's load event you perform your transition smoothly.
If the loading of the image typically takes more than 2 seconds, consider giving the user a visual clue the button click was recorded and stuff is about to happen.
This would be the technically correct solution.
Solution 2.
An alternative, used extensively in production environments, (and having nothing to do with Vue per-se), is to load thumbnails of the images in your page (as in 10 times smaller - which is ~100 times smaller in size, being areas). It might not be obvious, but they make very good substitutes for large ones, while the transition is ongoing - you might also want to experiment with CSS blur filter on them.
The thumbnail and the large image have to be perfectly overlapped, in the same transitioning parent, with the large one underneath. On load event of the large version, fade the thumbnail out, causing a focus-in effect: a subtle eye-catcher.
A rather funny perk of this method is that, if you leave the thumbs on top (with opacity: 0), whenever someone tries to download an image they right click the thumb and have trouble understanding why they're downloading it at such a low res.
Everything is pretty much the same (in terms of animations) with the addition of a focus-in effect on the image, when it actually loaded.
Not DRY, but efficient. Looks professional, everything seems to load instantly and smoothly. Overall, it converts way more than other "correct" solutions. Which is why it's commonly used on websites where page performance is important.
When dealing with visuals, being perfect is looking perfect (and, in UX, feeling/seeming perfect).
Ancient greeks bent their columns so they looked straight when watched from below.
Related
I have an image i want to load as the first thing when my page loads.
Currently i have an img tag with a src but it loads asynchronously, how can i assure the browser loads my image before rendering the page further?
A simple solution could be to use the preload attribute on <link>: Link types: preload. It is not a grantee that the content will be ready, as I read it, but maybe it fits your case.
Here is an example:
<head>
<link rel="preload" href="https://via.placeholder.com/450" as="image"/>
</head>
<body>
<img src="https://via.placeholder.com/450" />
</body>
I have done something similare ...
I have a 'splash screen' (you can use a simple white div to simulate no rendering ...)
Make a fetch to the img in js, then remove your splash screen ...
Fetch the img in js load the img in browser cache... so when your DOM display the img, she already in the browser cache ...
I created an account a very long time ago but I never really use it, as I always manage to find the answers to my questions in already solved threads. So, this is the first time since I'm working on my current program that I was not able to find a working answer on SO. However, if I simply missed it, please be nice to me ^^
A bit of context that may explain why I can't manage to apply any given solution despite many threads exists about this... I am doing:
A HTA Interface
With an iframe to a local text file
And a link to a js file that I want to use to refresh the iframe every 500 ms.
The goal is to make a chat; people can write on the text file and it automatically appears on the HTA interface.
I don't know ANYTHING about JS, frankly. So I found here that piece of code to refresh the iframe.
window.setInterval(function() {
document.getElementById('chatbox').contentWindow.location.reload();
}, 500);
It works, but upon refreshing the iframe, it scrolls back to the top; which makes the chat unreadable, as you can guess. Many solutions that I've read on SO won't work for me, maybe because it doesn't fit with this way of refreshing iframes, or with HTA, or I'm just too dumb with js to know how to make them work.
If anyone has a solution that I could just copy and use, even if I don't understand what I'm doing - it won't be very satisfying intellectually but at least I could focus on finishing my interface :) thanks a lot!
If you refresh the iframe by updating innerHTML from the text file, instead of doing a reload, it won't change the scroll position. Here's an example HTA:
<!DOCTYPE html>
<meta http-equiv="x-ua-compatible" content="ie=9">
<html>
<head>
<script>
function Refresh() {
var Iframe = document.getElementById("chatbox").contentWindow;
window.setInterval(function() {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fh = fso.OpenTextFile(".\\Chat.txt",1);
var FileContents = fh.AtEndOfStream ? "" : fh.ReadAll();
fh.Close();
Iframe.document.body.innerHTML = "<pre>" + FileContents + "</pre>"
}, 500);
}
</script>
<style>
#chatbox {height:30em;}
</style>
</head>
<body onload="Refresh()">
<iframe id=chatbox title="Chat Box">
</iframe>
</body>
</html>
I don't like lazy-loading because it looks quite ugly for the user in terms of UX. However, I understand the benefits (faster page load, low bandwidth, high PageSpeed scores etc.)
I am planning to write a javascript code which will:
Stay in the bottom > Wait for the page to fully load > After 3 seconds it will work > Force load all the images which were lazy-loaded previously during initial page load
With this, I won't lose any speed scores (because it will load as normal with lazy loaded images) But, also will load the full images when everything is done, so user won't notice. (we do the same for loading live chat scripts. It works pretty nice)
It will probably look something like this:
<script>
window.onload = function() {
setTimeout(function(){
var ForceLoadImages = document.createElement('script');
ForceLoadImages.type = 'text/javascript';
ForceLoadImages.src = 'link-to-the-script-to-force-load-images.js';
document.body.appendChild(chatScript);
}, 3000);
};
</script>
I am not a js expert. This was just a dummy example to give an idea. I am specifically asking how to write the content of that "link-to-the-script-to-force-load-images.js" part. Will appreciate any inputs.
There was a similar question here, but need an answer for Wordpress.
I guess that the wp lazy load uses data-src attribute to hold the image and when in view port, its adding the image to the src attribute.
Simplified like this:
<img data-src="URL"/>
*if its not like this, find in your source code the attribute where image is hold when out of view
What you need to do is select all images and change the data-src to src like this:
var images = document.querySelectorAll('img');
window.onload = function() {
setTimeout(function(){
for (var i=0; i<images.length; i++) {
if(images[i].getAttribute('data-src')) {
images[i].setAttribute('src',images[i].getAttribute('data-src'));
images[i].removeAttribute('data-src'); // optional if you need to remove data-src attribute after setting src
}
}
}, 3000);
};
<div class="container">
<img data-src='https://picsum.photos/id/237/200/300' />
<img data-src='https://picsum.photos/seed/picsum/200/300' />
<img data-src='https://picsum.photos/200/300' />
</div>
I just wanted to put the solution as an answer (thanks to kaize) who are looking for something like this. It is very clean and works nice:
<script>
var loadimages = document.querySelectorAll('img');
window.onload = function() {
setTimeout(function(){
//Force Load images
for (var i=0; i<loadimages.length; i++) {
if(loadimages[i].getAttribute('loading')) {
loadimages[i].removeAttribute('loading');
}
}
}, 3000);
};
</script>
Who is this for?
For those who does not like the user-experience of lazy loading images. But applying it due to PageSpeed scores.
Where to place this?
Place this to the bottom of your page.
What does this script do?
This script runs 3 seconds after the page loaded completely. It force-loads all the images which were waiting to get into the viewport to be lazy-loaded by removing the "loading" attribute. So that, you get good pagespeed scores for deferring image loads meanwhile keeping the user experience better. (Keep in mind that you lose the bandwidth advantage of lazy-loading concept)
I have a web site containing millions of photos from thousands of events over the last 26 years, and the directory structure is \root\events\(year)\(eventdate) so that all the photos and files for any given event date are kept together. Each directory has its own index.shtml file which sorts out the organisation of the content.
I want to add a mobile version of the site. My current thinking is to add a mobile.shtml page to each directory, and switch based on a simple screen width test. All the pages throughout the site use a common header include, so I need to add code to a common header, but which will only switch if the current page is the index.shtml file. I´ve used the following, and it fails to switch.
This is what I´ve added to the include file
......
<body>
<script>
var url = location.href;
var filename = url.substring(url.lastIndexOf('/')+1);
var a = "index.shtml";
if((screen.width < 600) && (filename.equals(a))) {
location.href = 'mobile.shtml';
}
</script>
.........
I´m not a JS programmer and am trying to implement code seen hereabouts, so would appreciate learning why this fails! Thanks in advance to anyone that replies.
I have not used this for a while but I am pretty sure it still works. Place this in the Head of your HTML e.g. desktop website - index.html
<!-- Detect and redirect for mobile -->
<script type="text/javascript">
if(navigator.userAgent.match(/iP(od|hone)/i) || navigator.userAgent.match(/Android/i)){
if(document.cookie.indexOf("iphone_redirect=false")==-1) {
window.location.href="http://myWebsite.com/index_mobile.html";
}
}
</script>
Be sure to add your correct redirect here in this line from above:
window.location.href="http://myWebsite.com/index_mobile.html";
As a second solution to support your original intentions, I managed to get a variation of your JS code working live here DEMO. My DEMO will take you to GitHub if you view it from a screen with a width < or = to 600px. To test it properly you must view it from monitor sizes above and below 600px widths. Below is my variation of your JS code:
<body>
<script>
var url = window.location.href;
var filename = url.substring(url.lastIndexOf('/')+1);
var a = 'index.shtml';
if((screen.width <= 600) && (filename == a)) {
window.location.href = 'https://github.com/';
}
</script>
* NOTE the difference that my code will redirect you to GitHub as opposed to 'mobile.shtml'
What you are trying to accomplish should be done with responsive design techniques. In a perfect world, you could just use media queries
to adjust the css applied to format the content differently.
But I have also had the need sometimes to do server side detection of the client, because I would render an entirely different widget based on the form factor. You seem to be using .shtml, which is a poor choice IMHO. Your webserver at a min must support php, and that is a way more powerful server side choice for your app. PhpMobileDetect is a great script that will give you a way to detect if it is mobile, tablet, or desk. Problem now is that we have enormous phones, or phablets. But it might just be the right fit for your task.
This may be a dumb question, but I've a real confusion and want to get an opinion from somebody who knows this in-out.
Preloading images can be done either via JavaScript or CSS (to name two which I'm considering). I read the tutorials that if the browser finds the same image path again, it would render the cached image.
If I preload images like:
<img src="../images/bg.jpg" alt="background" width="1" height="1" style='display:none' />
and
<img src="images/bg.jpg" alt="background" />
Similar with javascript:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(){
$('<img/>')[0].src = this;
});
}
// Usage:
preload([
'../img/imageName.jpg',
'img/imageName.jpg' // case when using this script in a different hierarchical level)
]);
Will the second call result into rendering of the image from the cached version or it will not work because the image path specified is different (though ultimately it refers to the same file).
Thanks in advance.
I realise this is and old one but I get this one all the time from interns - so here goes...
Even though the onload function is in the JS file asking/telling the browser to look for the image; it is the browser looking for the image and is telling the JS that the image/s loaded.
So your image path in the JS should be the same as how you would enter it in the HTML.
PS: I noticed in your HTML the image folder is "/images" and in your JS the folder is "/img"