I am using jQuery to change a background image every second. This is working as I want, however, when I look at the NETWORK tab in Google chrome I see that a new GET request is made for the image every second which is obviously very uneconomical. Can anyone advise a better approach to take here. I have read on other posts that the images should be cached and that the browser should know not to make a request to the server, but as far as I can tell the browser is still making the request.
Please see current code below.
var x = 0,
homepageImages = ["1","2","3","4"];
setInterval(function(){
x++;
if(x === 4){
x = 0;
}
$('.desktop-main-img').attr('src', 'img' + homepageImages[x] + '.jpg');
}, 1000);
You want to preload the images then.
var x = 0,
homepageImages = ["1", "2", "3", "4"];
// invisible preload the images
for( var i = 0; i < homepageImages.length; i++ )
$(new Image()).src('img' + homepageImages[i] + '.jpg');
setInterval(function() {
x = ++x === 4 ? 0 : x;
$('.desktop-main-img').attr('src', 'img' + homepageImages[x] + '.jpg');
}, 1000);
But keep in mind, the network tab may show a get request for the images. But you should see, that the response is cached. The server tell your browser, that the image has not changes/he already knowns the image and will transfer no data.
you must preload all images in body before run that loop
Related
I have a JS code, that gets called in different web pages of my clients. I want to fetch the total number of images. I want only those images that are visible to the user and not just any other images. This is my JS code
function getImageCount(topWindow) {
try {
var images = topWindow.document.getElementsByTagName('img');
var imageCount;
for (var i=0, length = images.length; i < length; i++) {
var image = images[i];
var clientWidth = image.clientWidth;
if(clientWidth && clientWidth > 1) {
var src = image.getAttribute('src');
if(src) {
src = src.toLowerCase();
if(src.indexOf('.jpg') !== -1 ||
src.indexOf('.jpeg') !== -1 ||
src.indexOf('.gif') !== -1 ||
src.indexOf('png') !== -1) {
imageCount = imageCount ? ++imageCount : 1;
}
}
}
}
return imageCount;
} catch (e) {
processError("getImageCount", e);
}
}
var imageCount = getImageCount(top);
I have been trying a lot to stabilize this code so that it works correctly across all different types of web pages. Basically what I want is a generic code that captures image counts correctly.
Eg:
My code gives image count as 1 for http://www.msn.com/en-us/news/other/one-free-agent-every-nfl-team-should-sign-this-offseason/ss-AAmLlC0#image
What I want is a GENERIC CODE that gives me a correct image count irrespective of where it runs. Can some one give me some detailed solutions.
I would appreciate a lot.
To simplt count all the images (<img>) on the page:
document.images.length
And to count all the "visible" images (ones with width and height):
[...document.images].filter(img => img.clientWidth && img.clientHeight).length
This will give you the number of images on the page. This does not include CSS images. since your code didn't either then I take it you want <img> ones
I didn't quite understand the meaning of irrespective of where it runs.. can you elaborate?
// Extract images
websiteImages = document.getElementsByTagName('img');
for (url in websiteImages)
console.log(websiteImages.length);
//Extract inbound and outbound links
Links = document.querySelectorAll('a');
for (link in Links)
console.log(Links[link].href);
Paste this Scripts into your console of browser
Check on the Below Link/ any Link you like
(https://news.google.com/topstories?hl=en-IN&gl=IN&ceid=IN:en&gl=IN&ceid=IN:en)
The above-mentioned script will give all the Images present in the webpages.
And the second script will give all the number of Inbound and Outbound/exit links
Just apply Some filter as per your use case and you will be good to go.
Use this simple approach for links
$$('a').length
To count the number of images on a webpage use the below code:
$$('img').length
I'm creating a 'catch falling items' game, and have successfully drawn the 'catcher' image and the 'falling items' images onto the canvas. But I'd like to also show an image (x.png) when a falling image is not caught and hits the base of my html5 canvas. The generateX function is called from the animate function and when a falling image hits the base of the canvas. The sound.play() is working. The console.logs in the generateX function log the following - 2,3 | 1,3 | 1,3 - so I know that the image is not completely loaded the first time this function runs. Everything seems to be working EXCEPT for the actual image displaying. I tried to structure it in ways that other threads have suggested, but nothing is working. Any help is much appreciated!
generateX: function() {
console.log('inside generateX func');
var imgX = new Image();
imgX.src = 'assets/x.png';
function drawX() {
console.log(3);
counter+=20;
context.drawImage(imgX, xIcon.x + counter, xIcon.y, xIcon.width, xIcon.height);
xArr.push(imgX);
console.log('xArr', xArr);
}
if (imgX.complete) {
console.log(1);
drawX();
} else {
console.log(2);
imgX.onload = drawX;
}
helper.checkMisses();
}
animate: function() {
var sound;
if (continueAnimating) {
requestAnimationFrame(helper.animate);
}
for (var i = 0; i < surveys.length; i++) {
var survey = surveys[i];
if (helper.isColliding(survey, dinoSaurus)) {
sound = new Audio("audio/coinSound.mp3");
sound.play();
score += 5;
helper.resetSurvey(survey);
}
survey.y += survey.speed;
// if the survey is below the canvas,
if (survey.y > canvas.height) {
sound = new Audio("audio/buzzerSound.mp3");
sound.play();
helper.generateX();
helper.resetSurvey(survey);
}
}
// redraw everything
helper.drawAll();
}
--
resetSurvey: function(survey) {
// randomly position survey near the top of canvas
survey.x = Math.random() * (canvas.width - surveyWidth);
survey.y = 40 + Math.random() * 30;
survey.speed = (0.55 + Math.random()) * 0.9;
}
Try loading your image earlier
If I understand your situation correctly, I think the issue is that you need to load the image earlier so that it is easily ready by the time you want to draw it, rather than waiting to load it only inside generateX. The way you are currently coding it, you are loading the image (imgX.src = ...) and then immediately trying to draw it. True, you are checking if the loading is complete and, if not, trying again shortly after. However, a better strategy is to load the image much earlier, e.g. during game initialization.
Demo
The code snippet below demonstrates the difference that this makes. It loads 2 different images. The only difference between the two is that one (on the left) is loaded ahead of time and the other (on the right) is only loaded inside generateX. The former image displays properly the 1st time through the game cycle while the latter image is missing the 1st time and only displays properly the 2nd time through.
function loadLeftImageWellBeforeRunningGenerateX() { // correct timing
imgs[0] = new Image();
imgs[0].src = 'http://placehold.it/200x100.png';
}
function loadRightImageInsideGenerateX() { // incorrect timing
imgs[1] = new Image();
imgs[1].src = 'http://placehold.it/100x50.png';
}
function generateX() {
loadRightImageInsideGenerateX();
log('--inside generateX func');
imgs.forEach(function(imgX, num) { // same as your original code, just done for 2 different images
if (imgX.complete) {
log("----image #" + num + " (" + side[num] + "); " + 1 + ", i.e. image complete");
drawX(imgX, num);
} else {
log("----image #" + num + " (" + side[num] + "); " + 2 + ", i.e. image not complete");
imgX.onload = drawX(imgX, num);
}
});
}
function drawX(imgX, num) { // same as your original code, just allows placement of 2 images side-by-side
log("----image #" + num + " (" + side[num] + "); " + 3 + ", i.e. drawing image (" + prefix[num] + "successfully)");
context.drawImage(imgX, num * 150 + 10, 10, 100, 50);
if (num === 1) {prefix[1] = "";}
}
var
imgs = [],
numClicks = 0,
side = ["left", "right"],
prefix = ["", "un"],
button = document.querySelector("button"),
canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');
context.fillStyle = "yellow";
context.fillRect(0, 0, 300, 150);
// as far as game logic goes, the only important lines below are marked with arrows
// the rest is just "fluff" to make the demo more understandable to the user
button.addEventListener("click", function() {
numClicks += 1;
if (numClicks === 1) {
log("You must clear your browser's recent cache every time you run this snippet " +
"in order for it to demonstrate the problems and solutions " +
"associated with loading images in this code.");
button.innerHTML = "Clear browser cache and click here again to start game initialization";
} else if (numClicks === 2) {
loadLeftImageWellBeforeRunningGenerateX(); // <----- ***
log("Game initializes. No images should yet be visible, " +
"but image #0 (left) should be loading/loaded now, i.e. ahead of time. " +
"Image #1 (right) is not yet loading/loaded. Click again to 'play game'.");
button.innerHTML = "Do NOT clear the browser cache and click here again to start playing game";
} else {
button.innerHTML = "Do NOT clear the browser cache and click here again to continue playing game";
if (numClicks === 3) {
log("Game begins. Images are required for the first time. Only the pre-loaded left image shows " +
"even though loading both is attempted.");
} else {
log("Game continues. On second and subsequent re-draws, both images are now available and visible.");
}
generateX(); // <----- ***
}
});
function log(msg) {
document.body.appendChild(document.createElement("p")).innerHTML = msg;
}
p {
margin: 0.2em;
}
<canvas id="myCanvas" width="300" height="80"></canvas>
<div>
<button>Click here to begin demo</button>
</div>
Your browser's cache can hide this bug
By the way, if and when you try to debug this kind of thing, be aware that many/most/all browsers cache images, making it potentially difficult to replicate this kind of bug if you're not aware of what's going on. For example, you might run your buggy code and the image is missing the first time through the game cycle. You study your code, make a change to try to fix the bug and run the code a second time. Voila, it looks like the problem is fixed because the image now appears the first time through the game cycle. However, it may, in fact, only be showing up in that first cycle not because the bug is fixed but because the browser is able to use the cached image even in the first game cycle, eliminating the need to load it from its source. In short, if you're trying to debug an issue like this where you suspect the timing of file loading is important but the bug only appears sometimes, try clearing your browser's recent cache to see if that makes the bug more apparent.
To clear a browser's cache:
Firefox (for Mac or Windows, v44...): click "History" / click "Clear Recent History..." / click to open "Details" if necessary / check "Cache" / click "Clear now"
Chrome (for Mac, v48...) "Chrome" (left menu item) / "Clear Browsing Data..." / select "Cached images and files" / click "Clear browsing data"
Chrome (for Windows, v48...) click customize-and-control icon (at the right) / click "More tools..." / click "Clear browsing data..." / click "Clear browsing data"
Internet Explorer (for Windows, v11...) click "Tools" / click "Delete browsing history" / select "Download history" / click "Delete"
I have a web application where the user can generate PDF and PowerPoint files. These files may take some time to generate, so I would like to be able to display a loading animation while it generates. The problem here is that I have no mean to know when the download has started. The animation never goes away.
I am aware that it could be possible to generate the file "on the side" and alert the user when the file is ready for download using AJAX, but I prefer "locking" the user while he waits for the download to start.
To understand what needs to be done here, let's see what normally happens on this kind of request.
User clicks the button to request the file.
The file takes time to generate (the user gets no feedback).
The file is finished and starts to be sent to user.
What we would like to add is a feedback for the user to know what we are doing... Between step 1 and 2 we need to react to the click, and we need to find a way to detect when step 3 occurred to remove the visual feedback. We will not keep the user informed of the download status, their browser will do it as with any other download, we just want to tell the user that we are working on their request.
For the file-generation script to communicate with our requester page's script we will be using cookies, this will assure that we are not browser dependent on events, iframes or the like. After testing multiple solutions this seemed to be the most stable from IE7 to latest mobiles.
Step 1.5: Display graphical feedback.
We will use javascript to display a notification on-screen. I've opted for a simple transparent black overlay on the whole page to prevent the user to interact with other elements of the page as following a link might make him lose the possibility to receive the file.
$('#downloadLink').click(function() {
$('#fader').css('display', 'block');
});
#fader {
opacity: 0.5;
background: black;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="fader"></div>
Click me to receive file!
</body>
Step 3.5: Removing the graphical display.
The easy part is done, now we need to notify JavaScript that the file is being downloaded. When a file is sent to the browser, it is sent with the usual HTTP headers, this allows us to update the client cookies. We will leverage this feature to provide the proper visual feedback. Let's modify the code above, we will need to set the cookie's starting value, and listen to its modifications.
var setCookie = function(name, value, expiracy) {
var exdate = new Date();
exdate.setTime(exdate.getTime() + expiracy * 1000);
var c_value = escape(value) + ((expiracy == null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = name + "=" + c_value + '; path=/';
};
var getCookie = function(name) {
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == name) {
return y ? decodeURI(unescape(y.replace(/\+/g, ' '))) : y; //;//unescape(decodeURI(y));
}
}
};
$('#downloadLink').click(function() {
$('#fader').css('display', 'block');
setCookie('downloadStarted', 0, 100); //Expiration could be anything... As long as we reset the value
setTimeout(checkDownloadCookie, 1000); //Initiate the loop to check the cookie.
});
var downloadTimeout;
var checkDownloadCookie = function() {
if (getCookie("downloadStarted") == 1) {
setCookie("downloadStarted", "false", 100); //Expiration could be anything... As long as we reset the value
$('#fader').css('display', 'none');
} else {
downloadTimeout = setTimeout(checkDownloadCookie, 1000); //Re-run this function in 1 second.
}
};
#fader {
opacity: 0.5;
background: black;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="fader"></div>
Click me to receive file!
</body>
Ok, what have we added here. I've put the set/getCookie functions I use, I don't know if they are the best, but they work very well. We set the cookie value to 0 when we initiate the download, this will make sure that any other past executions will not interfere. We also initiate a "timeout loop" to check the value of the cookie every second. This is the most arguable part of the code, using a timeout to loop function calls waiting for the cookie change to happen may not be the best, but it has been the easiest way to implement this on all browsers. So, every second we check the cookie value and, if the value is set to 1 we hide the faded visual effect.
Changing the cookie server side
In PHP, one would do like so:
setCookie("downloadStarted", 1, time() + 20, '/', "", false, false);
In ASP.Net
Response.Cookies.Add(new HttpCookie("downloadStarted", "1") { Expires = DateTime.Now.AddSeconds(20) });
Name of the cookie is downloadStarted, its value is 1, it expires in NOW + 20seconds (we check every second so 20 is more than enough for that, change this value if you change the timeout value in the javascript), its path is on the whole domain (change this to your liking), its not secured as it contains no sensitive data and it is NOT HTTP only so our JavaScript can see it.
VoilĂ ! That sums it up. Please note that the code provided works perfectly on a production application I am working with but might not suit your exact needs, correct it to your taste.
This is a simplified version of Salketer's excellent answer. It simply checks for the existence of a cookie, without regard for its value.
Upon form submit it will poll for the cookie's presence every second. If the cookie exists, the download is still being processed. If it doesn't, the download is complete. There is a 2 minute timeout.
The HTML/JS page:
var downloadTimer; // reference to timer object
function startDownloadChecker(buttonId, imageId, timeout) {
var cookieName = "DownloadCompleteChecker";
var downloadTimerAttempts = timeout; // seconds
setCookie(cookieName, 0, downloadTimerAttempts);
// set timer to check for cookie every second
downloadTimer = window.setInterval(function () {
var cookie = getCookie(cookieName);
// if cookie doesn't exist, or attempts have expired, re-enable form
if ((typeof cookie === 'undefined') || (downloadTimerAttempts == 0)) {
$("#" + buttonId).removeAttr("disabled");
$("#" + imageId).hide();
window.clearInterval(downloadTimer);
expireCookie(cookieName);
}
downloadTimerAttempts--;
}, 1000);
}
// form submit event
$("#btnSubmit").click(function () {
$(this).attr("disabled", "disabled"); // disable form submit button
$("#imgLoading").show(); // show loading animation
startDownloadChecker("btnSubmit", "imgLoading", 120);
});
<form method="post">
...fields...
<button id="btnSubmit">Submit</button>
<img id="imgLoading" src="spinner.gif" style="display:none" />
</form>
Supporting Javascript to set/get/delete cookies:
function setCookie(name, value, expiresInSeconds) {
var exdate = new Date();
exdate.setTime(exdate.getTime() + expiresInSeconds * 1000);
var c_value = escape(value) + ((expiresInSeconds == null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = name + "=" + c_value + '; path=/';
};
function getCookie(name) {
var parts = document.cookie.split(name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
function expireCookie(name) {
document.cookie = encodeURIComponent(name) + "=; path=/; expires=" + new Date(0).toUTCString();
}
Server side code in ASP.Net:
...generate big document...
// attach expired cookie to response to signal download is complete
var cookie = new HttpCookie("DownloadCompleteChecker"); // same cookie name as above!
cookie.Expires = DateTime.Now.AddDays(-1d); // expires yesterday
HttpContext.Current.Response.Cookies.Add(cookie); // Add cookie to response headers
HttpContext.Current.Response.Flush(); // send response
Hope that helps! :)
You can fetch the file using ajax add indicator then create a tag with dataURI and click on it using JavaScript:
You will need help from this lib: https://github.com/henrya/js-jquery/tree/master/BinaryTransport
var link = document.createElement('a');
if (link.download != undefined) {
$('.download').each(function() {
var self = $(this);
self.click(function() {
$('.indicator').show();
var href = self.attr('href');
$.get(href, function(file) {
var dataURI = 'data:application/octet-stream;base64,' + btoa(file);
var fname = self.data('filename');
$('<a>' + fname +'</a>').attr({
download: fname,
href: dataURI
})[0].click();
$('.indicator').hide();
}, 'binary');
return false;
});
});
}
You can see download attribute support on caniuse
and in your html put this:
generate
I found the answer by jcubic worked really well for my needs.
I was generating a CSV (plain text) so did not need the additional binary library.
If you need to do the same, here is how I adjusted it to work for plain text files.
$('.download').on('click', function() {
// show the loading indicator
$('.indicator').show();
// get the filename
var fname = $(this).attr('data-download');
$.get("/products/export", function(file) {
var dataURI = 'data:text/csv;charset=utf-8,' + file;
$('<a>' + fname + '</a>').attr({
download: fname,
href: dataURI
})[0].click();
// hide the loading indicator
$('.indicator').hide();
});
});
The filename is specified with data-download attribute in HTML on the tag with .download class.
I want to display several images of the same size at the same position, one at a time, with a 5s interval between each change. To do so I've used jQuery.Timer, that uses setInterval() to call some show_next_image() function every 5s.
It actually does work with IE, Opera, Safara, Firefox and.. partly with Google Chrome. It's not working with Google Chrome if I open a new window and directly type my website URL: it'll show the second image and stop. And with any other situation (reload, from another link, not right after opening a new window) it'll badly work: one can see the back image before the front image is shown.
Thus I'm wondering whether I've done something wrong with my JavaScript source. What I do is I use a front and a back image. When I want to show the next image, the back img source is set to the new image, and the front image is faded out while the back one is faded in through jQuery. You can check it out at http://www.laurent-carbon.com/ (in French). The two img are identified with bg1 and bg2.
var images = ["/img/IMG_0435bg.jpg", "/img/IMG_0400bg.jpg", "/img/maisonnette 2.jpg", "/img/IMG_0383bg.jpg", "/img/IMG_0409bg.jpg", "/img/IMG_0384bg.jpg"];
var idx = 1;
var waitTime = 5000; // ms
$(document).ready(function() {
$("#bg2").hide();
$.timer(waitTime, load_next);
$.preLoadImages(images);
});
function load_next(timer) {
var toshow = images[idx];
idx++;
idx %= images.length;
back_image().attr('src', toshow);
swap_images();
}
function front_image() {
return (idx % 2 == 0) ? $("#bg1") : $("#bg2");
}
function back_image() {
return (idx % 2 == 0) ? $("#bg2") : $("#bg1");
}
function swap_images() {
back_image().fadeOut('slow');
front_image().fadeIn('slow');
}
Thanks,
Ceylo
Ok I've worked out a solution .... without the use of plugins.
Demo
http://jsfiddle.net/morrison/PvPXM/9/show
source
http://jsfiddle.net/morrison/PvPXM/9/
This approach is a lot cleaner and removes the problem I had while viewing your page in chrome: the animation getting out of sync and flashing.
The only thing you have to do in the HTML is wrap the two images in a <div id="fadeBox" style="position:relative"></div>
$(function() {
var images = [
"http://www.laurent-carbon.com/img/IMG_0435bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0400bg.jpg",
"http://www.laurent-carbon.com/img/maisonnette 2.jpg",
"http://www.laurent-carbon.com/img/IMG_0383bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0409bg.jpg",
"http://www.laurent-carbon.com/img/IMG_0384bg.jpg"
];
var idx = 1;
var max = images.length;
var easing = "swing";
var waitTime = 5000; // ms
var fadeTime = 2000; // ms
var fadeShow = function(fadeTime, fadeDelay) {
var $topImage = $("#fadeBox img:last");
$topImage.fadeTo(fadeDelay, 1, function() {
$topImage.fadeTo(fadeTime, 0, easing, function() {
$topImage
.fadeTo(0, 1)
.insertBefore("#fadeBox img:first")
.attr("src", images[++idx == max ? idx = 0 : idx]);
fadeShow(fadeTime, fadeDelay);
});
});
};
fadeShow(fadeTime, waitTime);
});
Hope this helps
PS thanks to Levi for cleaning the code up a bit.
Answer: http://jsfiddle.net/morrison/RxyZY/
Notes:
You are trying to reinvent the wheel. You are creating a simple slideshow. There are numerous plugins to do exactly this and much more. I used jQuery cycle in my example, which is extremely customizable.
You should wrap your stuff up in a function, creating an expression. In my example, the (function($){}(jQuery)) is what does the trick. It scopes your variables to the function, rather than the global namespace.
So in my script I have...
<script type="text/javascript">
var images = new Array();
var numImages = 3;
var index = 0;
function setupSwapper() {
for (i = 0; i < numImages; i++) {
images[i] = new Image(266, 217);
images[i].src = "images/image" + i + ".png";
}
setTimeout("swapImage()", 5000);
}
function swapImage() {
if (index >= numImages) {
index = 0;
}
document.getElementById('myImage').src = images[index].src
index++;
setTimeout("swapImage()", 5000);
}
</script>
And then I have <body onload="setupSwapper()"> to setup the body.
and <img width=266 height=217 id="myImage" name="myImage" src="images/image0.png"></img> elsewhere in my document.
Only the initial image (image0.png) is showing up. I'm probably blind from having looked at this so long. The images are not swapping.
Use FireBug or a similar tool for debugging what's going on:
Does the img DOM element in fact gets its src changed ?
Do you see any network activity trying to load the images ? does it succeed ?
Set up breakpoints in your code and see what happens in the debugger
BTW - You can use setInterval instead of setTimeout - it sets a repeating timer
You're missing the () in the definition of "setupSwapper".
Also it's setTimeout, not setTimeOut.
Finally, get rid of the "type" attribute on your <script> tag.
You might want to start "index" at 1 instead of 0.
The way to go:
setTimeout(swapImage, 5000);
[FORGET] the type attribute has nothing to do with it
[FORGET] the index has nothing to do with it
[OPTIONAL] remove "name" attribute from the image (useless)
[OPTIONAL] close image tags like <img />
Note: 2-5 is about correctness. Only the first one is important to get it work.
Get Firebug, use it's debugger to put breakpoints inside swapImage to see if it is hit after the timeout. Another way is to use the console.* apis to see what's happening(e.g. console.log).