I have a basic Node js express web application with webix UI on the front-end.
There are a lot of components/tables with a lot of data that needs to be populated in these tables.
I have a loading screen with some background-images changing background image URL every 3 secs until the loading of all the data is complete.
The problem with the loading screen is that the images are not the first thing that get downloaded by the browser. The tables sometimes populate even before the images get downloaded and it beats the whole purpose of having the loading screen.
I was wondering if there was a way I can load these images first thing when the application is opened in the browser.
Here is the HTML Code:
<body>
<div id="container-id" class="container">
<div id="text-id" class="text">Loading...</div>
</div>
</body>
Here is the CSS:
#-webkit-keyframes changeBg
{
0% {background-image: url("/resources/images/one.jpg");}
25% {background-image: url("/resources/images/two.jpg");}
50% {background-image: url("/resources/images/three.jpg");}
75% {background-image: url("/resources/images/four.jpg");}
100% {background-image: url("/resources/images/five.jpg");}
}
.container {
height: 100%;
width: 100%;
background-image: url("/resources/images/one.jpg");
-webkit-animation: changeBg 15s infinite;
background-size: 100%;
background-position: center;
}
Based on this similar question: load specific image before anything else
I tried loading the images using the javascript as first few lines in the index page as follows:
<head>
<script type="text/javascript">
(new Image()).src= window.location.href + "resources/images/one.jpg";
(new Image()).src= window.location.href + "resources/images/two.jpg";
(new Image()).src= window.location.href + "resources/images/three.jpg";
(new Image()).src= window.location.href + "resources/images/four.jpg";
(new Image()).src= window.location.href + "resources/images/five.jpg";
</script>
<link rel="stylesheet" href="webix.css" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="app.css" type="text/css" media="screen" charset="utf-8" />
</head>
But still, the other requests get served way before the images.
Actually scripts are loaded by order they are placed in the head. Browser won't wait for event to being raised then go for other scripts to load (It doesn't make sense). So your script is loaded completely but it's event for image loading are not raised.
So we have to await until all images are loaded then load scripts.
Here is the code:
<script type="text/javascript">
function loadImageAsync(url) {
return new Promise((resolve) => {
let img = new Image();
img.src = url;
img.addEventListener('load', e => resolve(img));
});
};
async function loadImages() {
var im1 = loadImageAsync("imageUrlOne");
var im2 = loadImageAsync("imageUrlTwo");
var image1 = await im1;
console.log(`image one loaded..., url:${image1.src}`)
var image2 = await im2
console.log(`image two loaded..., url:${image2.src}`)
}
function loadScript(url) {
var script = document.createElement("script")
script.type = "text/javascript";
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadImages().then(() => {
console.log("loading scripts...");
loadScript("script 1 url here");
loadScript("script 2 url here");
});
</script>
You can use react js for such things, Reactjs makes lots of thing like this easy.
To know more about react .
Visit: https://reactjs.org/
import React from "react";
class ImageWithStatusText extends React.Component {
constructor(props) {
super(props);
this.state = { imageStatus: "loading" };
}
handleImageLoaded() {
this.setState({ imageStatus: "loaded" });
}
handleImageErrored() {
this.setState({ imageStatus: "failed to load" });
}
render() {
return (
<div>
<img
src={this.props.imageUrl}
onLoad={this.handleImageLoaded.bind(this)}
onError={this.handleImageErrored.bind(this)}
/>
{this.state.imageStatus}
</div>
);
}
}
export default ImageWithStatusText;
Related
I'm detecting device orientation using window.matchMedia. The following code works as intended - every orientation change is logged to console with correct value:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Test App</title>
</head>
<body>
<script type="application/javascript">
let isIframe= () => {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
let onOrientationChange = () => {
const isLandscape = window.matchMedia("(orientation: landscape)").matches;
console.log("Event: " + (isIframe() ? "Iframe " : "") + "landscape:" + isLandscape);
}
let mediaQueryList = window.matchMedia("(orientation: landscape)");
console.log("Onload: " + (isIframe() ? "Iframe " : "") + "landscape:" + mediaQueryList.matches);
mediaQueryList.addListener(onOrientationChange);
</script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">Hello World in Iframe</div>
</body>
</html>
But when I run that page in iframe, callback registered using addListener is not fired. In iframe, I only get singular log line - Onload: Iframe landscape:true, regardless of the device orientation.
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">Hello World</div>
<iframe id="game" src="iframeContent.html" frameborder="0" style="width: 960px; height: 600px;"></iframe>
</body>
I'm using addListener instead of addEventListener, because the second one function is not working on all Safari versions.
Tested on Safari 14 and on Dev Tools of Chrome and Firefox.
My question is - why addListener callback is not invoked in iframe.
Thank you.
If the iframe does not get it's size to change because it has fixed width and height, thus resize related events cannot be triggered inside it including MediaQueryList events regarding orientation.
You can do two things to get this working; you can make your iFrame width and height to be 100%, or you can let the media query detection code inside the main window and pass the orientation state using postMessage when it triggers a change event.
1) Changing iFrame size to 100% so it resizes when landscape/portrait orientation event triggers
In the main page, make the body full height and the iframe full width/height (using CSS).
body {
height: 100vh;
margin: 0;
}
iframe {
width: 100%;
height: 100%;
}
Live example that you can test: https://zikro.gr/dbg/so/65704468/
2) Media query detection on the main page and use postMessage to send a message to iFrame when orientation event triggers
index.html:
<iframe src="iframe.html"></iframe>
<script>
let iframe = document.querySelector('iframe');
let onOrientationChange = () => {
iframe.contentWindow.postMessage({
isLandscape: window.matchMedia("(orientation: landscape)").matches
}, '*');
}
iframe.addEventListener('load', onOrientationChange);
const mediaQueryList = window.matchMedia("(orientation: landscape)");
mediaQueryList.addListener(onOrientationChange);
</script>
iframe.html:
<script>
window.addEventListener("message", (event) => {
if (event.data.isLandscape) {
console.log('iFrame Landscape');
} else {
console.log('iFrame Portrait');
}
});
</script>
Live example that you can test: https://zikro.gr/dbg/so/65704468/pm/
I am developing a website and I have a problem with lazy loading images.
It was taking too much time to load the images so I checked the Browser Network Tab and I saw that Safari was loading multiple times the same image, slowing down the loading of the other media.
I reproduced the code to make as simple as possible and when I lazy load images the browser load them twice.
<style type="text/css">
[data-src]{
opacity: 0;
transition: all .4s ease;
}
[data-src].vis{
opacity: 1;
}
</style>
<img data-src="https://via.placeholder.com/150/045b1f/ffff11" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b44/ffff22" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b77/ffff33" src="dot.png">
<img data-src="https://via.placeholder.com/150/045b00/ffff44" src="dot.png">
<script type="text/javascript">
function lazy(){
$('[data-src]').each(function(){
var $img = $(this);
var src = $img.attr('data-src');
var img = new Image();
img.src = src;
$(img).on('load', function(){
console.log('loaded',src);
$img.attr('src',this.src);
$img.addClass('vis');
});
});
}
jQuery(function($){
setTimeout(function(){
lazy();
},3000)
});
</script>
I also made a CodePen https://codepen.io/nbl7/pen/GaoZXW
On my website this happen only on Safari and it loads the same image even 10 times. Probably because I call the lazy load function different times.
On the code I provided it happens also on Chrome.
Should the browser know already which image has been loaded and cache them without making new requests?
Thanks for the help.
Here's a code that doesn't load the images twice:
function lazy() {
$('[data-src]').each(function() {
var img = this;
var src = $(img).attr('data-src');
$(img).attr('src', src);
$(img).on('load', function() {
console.log('loaded', src);
$(img).addClass('vis');
});
});
}
jQuery(function($) {
// lazy();
setTimeout(function() {
lazy();
}, 3000)
});
[data-src] {
opacity: 0;
transition: all .4s ease;
}
[data-src].vis {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img data-src="https://via.placeholder.com/150/045b1f/ffff11" src="">
<img data-src="https://via.placeholder.com/150/045b44/ffff22" src="">
<img data-src="https://via.placeholder.com/150/045b77/ffff33" src="">
<img data-src="https://via.placeholder.com/150/045b00/ffff44" src="">
You set the img src twice in your loop, and that means that it loads twice:
on this line: img.src = src; (line 6)
then on this line: $img.attr('src',this.src); (line 9)
I have researched and experimented my brains out on this one. I have built a working application that uses the HTML5 canvas to play videos and interact with users through a web page (a game, sort of). Works like a charm, but I want to add captions to the video to make it more accessible. I don't even seem to be able to get the WebVTT file to load and I've reached the stage of voodoo programming by trying examples from the web and am making no progress. I have tried to distill the application down to its bare minimum here in hopes someone can provide some insight into what I'm doing wrong.
The video plays when I click on the canvas, the "...waiting for cuechange event..." message stays up while it plays, and then it goes to the "video complete ... reload to try again" message when done (kept it simple by requiring a page reload to try the test again), so the basic mechanics seem to be working. It never gets into the "load" function for the text track (never displays "... loaded text track ..."), and the updateSubtitle() function is never invoked. If I insert a trackElement.readyStatus() call, it always returns 0 (unsurprisingly). As a further note, if I add a testStatus.innerHTML = "...loaded metadata..."; statement in the "loadedmetadata" listener, it does get there.
var canvasContext, videoElement, intervalHandle, testStatus, trackElement;
function processFrame() {
canvasContext.drawImage(videoElement, 193, 50, 256, 194);
}
function videoEnded() {
clearInterval(intervalHandle);
testStatus.innerHTML = "video complete ... reload to try again";
}
var count = 0;
function updateSubtitle(event) {
count = count + 1;
testStatus.innerHTML = "count" + count;
}
function clickHandler(event) {
testStatus.innerHTML = "...waiting for cuechange event...";
videoElement.setAttribute("src", "start.ogg");
videoElement.load();
intervalHandle = setInterval(processFrame, 25);
videoElement.play();
}
function init() {
var canvasElement, textTrack;
canvasElement = document.createElement("canvas");
videoElement = document.createElement("video");
videoElement.addEventListener("loadedmetadata", function() {
trackElement = document.createElement("track");
trackElement.kind = "captions";
trackElement.label = "English";
trackElement.srclang = "en";
trackElement.src = "start_en.vtt";
trackElement.addEventListener("load", function() {
testStatus.innerHTML = "... loaded text track ...";
trackElement.mode = "showing";
});
videoElement.appendChild(trackElement);
trackElement.addEventListener("cuechange", updateSubtitle, false);
});
var mainDiv = document.getElementById("arghMainDiv");
canvasElement.setAttribute("id", "mediaScreen");
canvasElement.width = 640;
canvasElement.height = 480;
var firstChild;
if(mainDiv.hasChildNodes()) {
firstChild = mainDiv.firstChild;
mainDiv.insertBefore(canvasElement, firstChild);
} else {
firstChild = mainDiv.appendChild(canvasElement);
}
testStatus = document.createElement("p");
testStatus.setAttribute("id", "testStatus");
mainDiv.insertBefore(testStatus, firstChild);
testStatus.innerHTML = "click on canvas to test";
canvasContext = canvasElement.getContext('2d');
canvasElement.addEventListener("click", clickHandler);
videoElement.addEventListener("ended", videoEnded);
}
#fatalError {
color: red;
}
#arghMainDiv {
text-align: center;
}
#mediaScreen {
border: 5px solid #303030;
margin-left: auto;
margin-right: auto;
display: block;
cursor: default;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Problems With TextTrack Object</title>
<link rel="stylesheet" type="text/css" href="subargh.css">
<script src="subargh.js"></script>
</head>
<body onload="init()">
<div id="arghMainDiv">
</div>
</body>
</html>
If you want the video and WebVTT files I'm using (just a few seconds long), they are here and here (respectively). I should also mention that if I play the video using VLC, that it recognizes and plays the .vtt file as subtitles properly on the video (so it appears to be well-formed). I am running my tests on Firefox 57.0.4 (64-bit) on a Windows 7 system if that makes any difference (but I am under the impression that Firefox is mostly fixed where Timed Text Tracks are concerned now).
I have little knowledge of JavaScript and need some help. I have this code (below) which when the countdown finishes, it replaces the image with a new one.
I wanted to modify this so that instead of a countdown - a button (img) is pressed instead. Here's my design to get an idea: Design.
I'm guessing I'll need 4 imageloaders instead of just one this time (one for each box) and some code to make it so that when the "older ->" button is clicked it triggers the loading of images?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript" charset="UTF-8"></script>
<script src="js/jquery.countdown.js" type="text/javascript" charset="UTF-8"></script>
<script type="text/javascript">
<!-- The ready() function will force the browser to run wait running the javascript until the entire page has loaded -->
$(document).ready(function() {
// The jquery code for constructing and starting the counter
// has been wrapped inside a function so we can call it whenever we want to
function startCounter(imageLoader){
$('#counter_2').countdown({
image: 'img/digits.png',
startTime: '00:03',
timerEnd: function(){ callback(imageLoader) },
format: 'mm:ss'
})
}
// Takes care of whatever need to be done every time
function callback(imageLoader){
// Replace image
$('#image').attr('src', imageLoader.nextImage());
// Clear the finished counter, so a new can be constructed in its place
$('#counter_2').empty();
// Construct a new counter and starts it
startCounter(imageLoader);
}
function ImageLoader(images){
this.images = images;
this.current = 0;
this.nextImage = function(){
this.current = (this.current+1) % this.images.length;
return this.images[this.current];;
}
}
// Fill in images in this array
imageLoader = new ImageLoader(['img/turd.png', 'img/guitar.gif', 'img/carrot.jpg', 'img/puppy.jpg']);
// Set everything off! (Construct a new counter and starts it)
startCounter(imageLoader);
});
</script>
<style type="text/css">
div.counter{
font-size: 36px;
font-weight: bold;
line-height: 77px;
}
</style>
</head>
<body>
<div id="counter_2" class="counter"></div>
<img id="image" src="img/turd.png" alt="Hot steaming turd">
</body>
</html>
Thanks everyone, I'm really stuck and really appreciate your help.
I'm using jquery.load to pull in a fragment of html from another page. The fragment contains quite a large background image. Once the load function has finished and it calls it's callback I set the fragment to display block in the page - problem is that as the html loads I see the new content without the background image....the background image loads later.
Whats a good method of making sure the image is loaded before I show the ajax content?
You could do this...
$('button').click(function() {
$('#element').load('/echo/html/', function(responseText) {
// For testing
responseText = '<link href="http://sstatic.net/stackoverflow/all.css?v=ded66dc6482e" rel="stylesheet" type="text/css" /><div class="ac_loading" style="width: 200px; height: 200px;">ABC</div>';
var element = $(this),
responseTemp = $('<div />').hide().html(responseText).appendTo('body'),
styles = responseTemp.find('link[type="text/css"]'),
stylesHook = $('head link[type="text/css"]:last');
if (stylesHook.length === 0) {
stylesHook = $('head *:last-child');
}
styles.insertAfter(stylesHook);
preloadSrc = responseTemp.find('div').css('backgroundImage').replace(/^url\(["']?(.*?)["']?\)$/, '$1'), image = new Image();
image.onload = function() {
styles.add(responseTemp).remove();
element.html(responseText);
}
image.src = preloadSrc;
});
});
jsFiddle.