I have created a simple image slideshow for my website. Basically, it's just a Javascript function that runs every 5 seconds and updates a div element's CSS "background-image" property. It works good, however, I've noticed that it contacts the server every time that the function runs to verify the image is in the cache. This produces a 304 code from the server each time.
The images will certainly be in the cache since they are images that are already contained somewhere else on the same page. Therefore, they are already loaded into the cache when the website loads originally.
Here is a simplified sample of my code. As you can see, the image URL is just being pulled right from an img element already loaded on the page:
function update(img) {
var slideshow = document.getElementById("slideshow");
slideshow.style.backgroundImage = 'url("' + image + '")';
}
function advance() {
var img = document.getElementsByClassName("slidesource")[index].src;
update(img);
index++;
}
var index = 0;
advance();
setInterval(advance,5000);
Is there a way to update the CSS property without the browser having to verify that the images are in the cache?
Verifying that they exist wastes internet data (albeit only around 1.5kB per request) and will cause the slideshow to stop working if the internet is disconnected, even if the images are already in the cache.
I couldn't replicate your issue, trying your code with some img tags and a div tag.
However, I sort of could replicate if if I manually turned off cache in Chrome and turned it on again. Anyway, with the following code below, I didn't get any issues with the images trying to load again from the server.
CSS Solution
<style>
#slideshow {
width: 400px;
height: 400px;
}
.img1 {
background-image: url("https://picsum.photos/400/400")
}
.img2 {
background-image: url("https://picsum.photos/400/401")
}
.img3 {
background-image: url("https://picsum.photos/400/402")
}
</style>
<body>
<div id="slideshow"></div>
</body>
<script>
function update(imgclassName) {
var slideshow = document.getElementById("slideshow");
slideshow.classList.remove( slideshow.classList[0] );
slideshow.classList.add(imgclassName);
}
function advance() {
update('img'+index);
index++;
if (index > NUMBER_OF_IMAGES) {
index = 1;
}
}
var NUMBER_OF_IMAGES = 3;
var index = 1;
advance();
setInterval(advance,1000);
</script>
Related
I have the following 2 functions:
function setPanelTo(panel,id){
$.getJSON("https://e926.net/posts/"+id+".json?_client="+clientname, function( data ) {
var img = document.createElement("img")
img.classList.add("panel")
img.src = data.post.file.url;
comic.append(img)
});
}
function getPoolByID(id){
comic.innerHTML = "";
$.getJSON("https://e926.net/pools/"+id+".json?_client="+clientname, function( data ) {
for (var x = 0; x < data.post_ids.length; x++){
setPanelTo(x,data.post_ids[x]);
}
});
}
and when I call the getPoolByID(); function, it populates the 'comic' <div> element with images. However, the images do not appear in the correct order, which slightly undermines the point of a comic.
How do I make it so that the images don't load until the previous image has loaded?
Note: I've tried using a table, and while it worked, the images did not wrap when they reached the edge of the page, which is a must have for this.
Use Image Placeholders
There are a few ways to solve the problem. The simplest solution is to modify the code to generate placeholder images and assign each a sequence ID. This ensures that the comic panels are in the correct order. And as the getJSON() for each image completes it updates the image src with the actual url.
Request Throttling
The API documentation says:
E621/E926 have a hard rate limit of two requests per second. This is a
hard upper limit and if you are hitting it, you are already going way
too fast. Hitting the rate limit will result in a 503 HTTP response
code. You should make a best effort not to make more than one request
per second over a sustained period.
And your loop to pull images hits this limit, which is why not all of the images are returned. So you may also need to add a timer to the loop to slow down the request rate, but this is outside the scope of the original question.
See related question for ideas: Ways to throttle ajax requests
Run the code snippet to understand how it works:
function setPanelTo(panel,id){
// Here we create an image placeholder BEFORE calling getJSON().
comic.innerHTML +=
'<img id="id' + id + '" src="https://via.placeholder.com/100" />';
$.getJSON("https://e926.net/posts/"+id+".json?_client="+clientname, function( data ) {
// And then we update each image once we have the actual url
document.querySelector("#id" + id).src = data.post.file.url;
});
}
function getPoolByID(id){
comic.innerHTML = "";
$.getJSON("https://e926.net/pools/"+id+".json?_client="+clientname, function( data ) {
for (var x = 0; x < data.post_ids.length; x++){
setPanelTo(x,data.post_ids[x]);
}
});
}
// Test
let clientname = "noname";
getPoolByID("30251", "");
img {
height: 100px;
width: auto;
margin: 2px;
border: 1px solid black;
}
Image source: https://e926.net/pools/30251
<div id="comic"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I am trying to make multiple images (3) fade in and out as parallax background. I am currently using a large animated gif which is not going to cut it due to the loading times and what I eventually need. I am trying to target a "data-background" attribute which I have done but can't seem to get the images to change. I can get it to output in the console but not the data-background. Below is the code.
Thanks!
<section id="paralax-image" style="height: 400px;" class="module-cover parallax" data-background="" data-overlay="1"
data-gradient="1">
(function () {
// The images array.
var images = ["assets2/Arcadian.jpg", "assets2/AngryPrawns.jpg", "assets2/Apricot_Smash.jpg"];
// The counter function using a closure.
var add = (function() {
// Setting the counter to the last image so it will start with the first image in the array.
var counter = images.length - 1;
return function() {
// When the last image is shown reset the counter else increment the counter.
if(counter === images.length - 1) {
counter = 0;
} else {
counter+=1;
}
return counter;
}
})();
// The function for changing the images.
setInterval(
function() {
var section = document.getElementById("paralax-image");
section.getAttribute("data-background");
section.setAttribute('data-background', images[add()]);
console.log(images[add()]);
}
, 3000);
})();
First of all, attributes that have "data-" in front of them are only used to store some custom data on elements. Those attributes do not influence the appearance/behaviour of your app in any way unless you use them in your JS/CSS.
So, in your code, you are setting the data-background attribute on your section. The code is working correctly and if you look into the inspector, you can actually see that that attribute's value is changing as expected.
The next step for you would be to display the images that you set in your data-background attribute - either using JS or CSS.
Unfortunately, for now, it's not possible to grab the background URL from attribute value in CSS as described in the top-voted answer here: Using HTML data-attribute to set CSS background-image url
However, you can still manually set the CSS background-image property using JavaScript based on the "data-" property.
// The images array.
const images = ["https://images.pexels.com/photos/255379/pexels-photo-255379.jpeg?auto=compress&cs=tinysrgb&h=350", "https://images.pexels.com/photos/531880/pexels-photo-531880.jpeg?auto=compress&cs=tinysrgb&h=350", "https://images.unsplash.com/photo-1530482817083-29ae4b92ff15?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=44f4aebbd1e1371d5bf7dc22016c5d29&w=1000&q=80"];
const imagesSwitcher = () => {
const getCounter = () => {
// Setting the counter to the last image so it will start with the first image in the array.
let counter = images.length - 1;
return () => {
// When the last image is shown reset the counter else increment the counter.
if(counter === images.length - 1) {
counter = 0;
} else {
counter += 1;
}
return counter;
}
}
const counter = getCounter();
const updateBackground = () => {
const section = document.getElementById("paralax-image");
section.style.background = `url(${images[counter()]}) no-repeat`;
};
updateBackground();
setInterval(() => updateBackground(), 3000);
};
imagesSwitcher();
.dynamic-background {
display: block;
width: 500px;
height: 200px;
background-size: 100%;
}
<div>
<section id="paralax-image" class="dynamic-background" style="height: 400px;" class="module-cover parallax" data-background="" data-overlay="1" data-gradient="1">
</section>
</div>
The thing is - in this case, you don't even actually need this data-background property. You can simply switch background image using JS.
Now, it's not very clear what you meant by parallax in your case. In case you actually meant parallax background like in here http://jsfiddle.net/Birdlaw/ny8rqzu5/, you would need to take a different approach overall. Please comment if you need any help with this.
This question already has answers here:
Load Random Images from Directory
(5 answers)
Closed 7 years ago.
I currently have a stagnant image on my site:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/stylesheets/normalize.css" media="all" rel="stylesheet" />
<link href="/stylesheets/master.css" media="all" rel="stylesheet" />
<script>
window.onload = function () {
var images = [],
i=1, indexImages = true,
prefix = '../image/',
extension = '.jpg';
while (indexImages) {
var a = new XMLHttpRequest(); a.open('GET', prefix+i+extension, false); a.send();
if (a.status != 404) { i += 1; images.push(prefix+i+extension); } else {
indexImages = false;
localStorage['backgroundIndex'] = !localStorage['backgroundIndex']?0:+localStorage['backgroundIndex']+2>images.length?0:+localStorage['backgroundIndex']+1;
document.body.style.backgroundImage = 'url(' + images[+localStorage['backgroundIndex']] + ')';
}
}
}
</script>
<style>
body {
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
background-color: black;
border-bottom: 8px solid #7D8A28;
}
</style>
</head>
<body>
<section id="card">
</section>
</body>
</html>
It's just that I want it to be a different image each time the page refreshes, so it auto changes to 2.jpg, 3.jpg, 10.jpg, whatever. (There are hundreds to choose from)
Could someone help me out with a solution? I'm not very good at this, and this is my first site.
Thanks.
I don't think CSS alone can do this, here's an answer:
Random
window.onload = function () {
var images = [
'image/1.png',
'image/2.png',
'image/3.png',
'image/4.png'
];
document.body.style.backgroundImage = 'url(' + images[Math.floor(Math.random()*images.length)] + ')';
}
This will load a random image every time you visit the page.
Specified Order
To load them in sequential order: First time image1, second time image2. The images don't need even have a number for this to work just fine. Do:
window.onload = function () {
var images = [
'image/A.png',
'image/B.png',
'image/C.png',
'image/D.png'
];
localStorage['backgroundIndex'] = !localStorage['backgroundIndex']?0:+localStorage['backgroundIndex']+2>images.length?0:+localStorage['backgroundIndex']+1;
document.body.style.backgroundImage = 'url(' + images[+localStorage['backgroundIndex']] + ')';
}
Generating the Array (ONLY IF YOUR IMAGES HAVE A NUMBER AT THE END)
This will automatically generate the array for you and you don't have to provide the amount of images
window.onload = function () {
var images = [],
i = 1,
prefix = 'image/',
extension = '.png',
max = 1000;
function index() {
var a = new XMLHttpRequest();
a.open('GET', prefix + i + extension, false);
a.send();
a.onreadystatechange = function () {
if (a.readyState === 4) {
if (a.status != 404) {
i += 1;
images.push(prefix + i + extension);
i < max ? index();
} else {}
localStorage['backgroundIndex'] = !localStorage['backgroundIndex'] ? 0 : +localStorage['backgroundIndex'] + 2 > images.length ? 0 : +localStorage['backgroundIndex'] + 1;
document.body.style.backgroundImage = 'url(' + images[+localStorage['backgroundIndex']] + ')';
}
};
}
index();
}
Most versatile solution
If you have PHP, this is probably the best solution in terms of working in many cases. But you really don't want to use PHP if you can avoid it. It will get all images in a directory to generate the array:
window.onload = function () {
var images = (JSON.parse("<?=scandir('../images')?>")||[]).filter(function (a) { return ['.', '..'].indexOf(a) < 0; });
document.body.style.backgroundImage = 'url(' + images[+localStorage['backgroundIndex']] + ')';
};
You can easily do it with JavaScript. You could define an array with all the images you have and then create a random number every time the page loads. Then use the random number to access the array's index to read the name of the image. Like so:
var images = ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg", "image5.jpg"];
var randomNumber = Math.floor((Math.random() * images.length));
//images[randomNumber] contains your random image
Hope it helps.
Regards,
I see all these JS solutions but most likely you need a php one as I am sure you don't wanna put the names of hundreds of images in a file. So if you have some knowledge of programming in php then try this.
Generate a random number. Let's say it is 42
Read the file names one by one (http://php.net/manual/en/function.readdir.php)
Once you reach the 42nd file, pass it to the page.
You can either put the filename directly in the css or have a php page as a background image source which returns the images. Either case, the above steps will give you what you want.
The above works well if file names and types are different. However, if file names are numerical and all files are the same extension then just generating a number and just using it as the file name is slightly faster than the above method.
However, it is still faster than most JS solution. Remember that JS is on client side. That mean it will be executed after that page is loaded. With JS, files need to be as minimal as possible. Otherwise, the page would load and you wouldn't have a background until that request is returned. Considering all these delays, I wouldn't call JS efficient over PHP.
Although you can always make it nice and fade it in or something :)
No need for AJAX
Why you should use PHP
The added benefit of using PHP is we can make it so all you have to do is drop an image into a folder and it will automatically be added to the randomized list! Which is great for maintainability. You code it once, and leave it alone!
In my opinion, there is no added benefit to using AJAX. Any time you add an image, you would have to add it to your JS image array. However, I don't know your exact scenario.
Important
All the other solutions that mention PHP, so far, have failed to mention one very important part... the page you are working in must be parsed as a PHP file.
There are two ways to do so:
Parse html/htm page as php page via .htaccess (advanced)
Change your the page extension to '.php' (simple)
(My solution requires that your page is parsed as PHP)
Solution 1
I would use JavaScript to randomly select the image from a PHP generated list, then append CSS to apply the background image to the page before the window.onload event.
PHP with JavaScript Random
Append the following to page head:
<script type="text/javascript">
<?php
// Path to image can be a relative or absolute path. I recommend absolute.
// The following is my absolute path.
$img_dir_path = '/rand-img/images/';
// Get directory list. If using absolute, prepend $_SERVER['DOCUMENT_ROOT'] for php
$dir_listing = scandir($_SERVER['DOCUMENT_ROOT'].$img_dir_path, 1);
// array_diff() to remove '.' & '..' from front of $dir_listing array
$img_array = array_diff( $dir_listing, array('.', '..') );
?>
// JavaScript variables
var img_dir_path = '<?php echo $img_dir_path ?>';
var img_array = <?php echo json_encode($img_array) ?>;
var rand_index = Math.floor(Math.random() * img_array.length);
var img_path = img_dir_path + img_array[rand_index];
// no need to wait for window.onload if we append actual style tag
var css = 'body { background-image: url('+img_path+') !important; }',
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
</script>
Using PHP for random, will only work if the page is loaded without cache.
However, using JavaScript will randomize the image even if the actual page is cached.
Solution 2
In my experience, this solution will randomize the image the vast majority of the time. However, if the page is loaded via cache, the background image will not be re-randomized.
PHP Random
Append the following to page head:
<?php
// Path to image can be a relative or absolute path. I recommend absolute.
// The following is my absolute path.
$img_dir_path = '/rand-img/images/';
// Get directory list. If using absolute, prepend $_SERVER['DOCUMENT_ROOT'] for php
$dir_listing = scandir($_SERVER['DOCUMENT_ROOT'].$img_dir_path, 1);
// array_diff() to remove '.' & '..' from front of $dir_listing array
$img_array = array_diff( $dir_listing, array('.', '..') );
// Random number based off size of $img_array
$random_index = mt_rand(0, count($img_array)-1);
// Build image path
$image_path = $img_dir_path . $dir_listing[ $random_index ];
?>
<style type="text/css">
body {
background-image: url("<?php echo $image_path ?>");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
background-color: black;
border-bottom: 8px solid #7D8A28;
}
</style>
I'm trying to make some JavaScript code to change the background of two div tags every X seconds. Here is my code:
HTML
<div id="bg_left"></div>
<div id="bg_right"></div>
CSS
body{
height:100%;
}
#bg_left{
height:100%;
width:50%;
left:0;
position:fixed;
background-position:left;
}
#bg_right{
height:100%;
width:50%;
right:0;
position:fixed;
background-image:url(http://presotto.daterrawebdev.com/d/img/pp_hey_you_bg.png);
background-position:right;
}
JAVA SCRIPT
function carousel_bg(id) {
var bgimgs = [ 'pp_hey_you_bg.png', 'burningman_bg.png' ];
var img1 = bgimgs[id];
var img2 = bgimgs[id+1];
var cnt = 2;
$('#bg_left').css("background-image", "url(http://presotto.daterrawebdev.com/d/img/"+img1+")");
$('#bg_right').css("background-image", "url(http://presotto.daterrawebdev.com/d/img/"+img2+")");
id = id + 1;
if (id==cnt) id = 0;
setTimeout("carousel_bg("+id+")", 10000);
}
$(document).ready(function() {
carousel_bg(0);
});
The background-images should be changing randomly, but they don't even change at all.
OK, I see the issue in your jsFiddle. Because you're passing a string to setTimeout() that string will be evaluated only at the top level scope. But, the function name you were passing is not at the top level scope (it's in an onload handler for the jsFiddle). So, I changed the way your JS is positioned in the jsFiddle so it is now at the top level scope. I also fixed up the logic for selecting an image and it now works here: http://jsfiddle.net/jfriend00/awVYP/
And, here's a cleaned up version that does not pass a string to setTimeout() (a much better way to write javascript) that passes a local function and uses a closure to keep track of the current index: http://jsfiddle.net/jfriend00/LVGNN/
function carousel_bg(id) {
var bgimgs = [ 'pp_hey_you_bg.png', 'burningman_bg.png' ]; // add images here..
function next() {
if (id >= bgimgs.length) {
id = 0;
}
var img1 = bgimgs[id];
id++;
if (id >= bgimgs.length){
id = 0;
}
var img2 = bgimgs[id];
$('#bg_left').css("background-image", "url(http://presotto.daterrawebdev.com/d/img/"+img1+")");
$('#bg_right').css("background-image", "url(http://presotto.daterrawebdev.com/d/img/"+img2+")");
setTimeout(next, 1000);
}
next();
}
$(document).ready(function() {
carousel_bg(0);
});
Previous comments on earlier version so of the OP's code:
$('#body')
should be:
$('body')
or even faster:
$(document.body)
Also, your jsFiddle shows a bit of an odd issue. Your CSS has a background image on the HTML tag, but your javascript sets a semi-transparent background image on the body tag. Is that really what you want?
For testing I added another image to the array so that we got some distinction in the sorting.
function carousel_bg(id) {
var bgimgs = [ 'http://presotto.daterrawebdev.com/d/img/pp_hey_you_bg.png', 'http://presotto.daterrawebdev.com/d/img/burningman_bg.png', 'http://gallery.orobouros.net/var/albums/2012/NewYorkComicCon2012/Legend-of-Korra/nycc_20121013_164625_0041.jpg?m=1354760251' ]; // add images here..
var img1 = bgimgs[id+1];
var img2 = bgimgs[id];
var cnt = bgimgs.length; // change this number when adding images..
$('#bg_left').css("background-image", "url("+img1+")");
$('#bg_right').css("background-image", "url("+img2+")");
id = id + 1;
if (id== (cnt - 1) ) id = 0;
setTimeout("carousel_bg("+id+")", 10000);
}
Two changes here:
For your total image count, I am retrieving the total count of images in the array dynamically instead of by hand (bgimgs.length)
In your conditional to reset the id value, subtract the total count by 1. Since JS has zero-based indexes, not doing this will get you an undefined error (a 3 item array will spit out a value of 4 in your original code on the last iteration).
While this code does loop through your array, it's not random. That's another topic.
For those not using JQuery, simply do the following:
document.body.style.backgroundImage="url(images/mybackgroundimage.jpg)";
I am using javascript to periodically replace a .png picture, which ist viewed fullscreen as the only content of a site. No matter how I try, in Firefox, after being loaded (as seen via firebug), the new image is always drawn from top to bottom. This takes some seconds. Is there any way to prevent this and show the picture all at once?
This is my current javascript code:
function preloadScreenshotPeriodically(){
var new_screenshot = new Image();
new_screenshot.src = "screenshot.png?defeat_firefox_caching=" + counter;
new_screenshot.id = "screenshot";
counter = counter + 1;
new_screenshot.onload = function(){
loadScreenshot(new_screenshot);
setTimeout("preloadScreenshotPeriodically();", 5000);
};
}
function loadScreenshot(new_screenshot){
document.getElementById("screenshot").parentNode.replaceChild(new_screenshot, document.screenshot);
}
I also tried to use two images, one of them hidden. Then loading the picture in the hidden one and swapping them. Same results :/
In an other version, I fetched the image with Ajax and after loading is complete, changed the url of the img-tag. My hope was, that the browser would recognize the picture had already been loaded and fetch it from the browsercache rather than loading it. But this didn't happen and I ended up with two requests to the server for one picture and the same slow drawing of it as in my other trys.
edit:
Now I tried it like suggested in answer 1. While it works just fine if I switch the picture when I load the next one (I don't want this), trying to switch it as soon as it is loaded (what I want) results in a blank window (very short) and visible loading of the picture as described above.
this works:
<body>
<style type="text/css">
#loaderWin { display:block; height:1px; width:1px; overflow:hidden; }
</style>
<div id="imagewin"></div>
<div id="loaderWin"></div>
<script type="text/javascript">
var screenshotCount=0;
function showFirstImage() {
loadNextImage();
}
function showNewImage() {
loadNextImage();
}
function nextImageLoaded() {
// swapImage();
}
function loadNextImage() {
swapImage();
screenshotCount = screenshotCount +1;
var nextImage = "<img id='loaderWinImg' src='screenshot.png?x="+screenshotCount+"' onload='nextImageLoaded()' />";
document.getElementById('loaderWin').innerHTML = nextImage;
}
function swapImage() {
document.getElementById("loaderWinImg").onload = '';
var newimage=document.getElementById('loaderWin').innerHTML;
document.getElementById('imagewin').innerHTML = newimage;
}
var showImages = setInterval("showNewImage()",15000);
showFirstImage();
</script>
</div>
</body>
</html>
this doesn't work:
<body>
<style type="text/css">
#loaderWin { display:block; height:1px; width:1px; overflow:hidden; }
</style>
<div id="imagewin"></div>
<div id="loaderWin"></div>
<script type="text/javascript">
var screenshotCount=0;
function showFirstImage() {
loadNextImage();
}
function showNewImage() {
loadNextImage();
}
function nextImageLoaded() {
swapImage();
}
function loadNextImage() {
screenshotCount = screenshotCount +1;
var nextImage = "<img id='loaderWinImg' src='screenshot.png?x="+screenshotCount+"' onload='nextImageLoaded()' />";
document.getElementById('loaderWin').innerHTML = nextImage;
}
function swapImage() {
// loadNextImage();
document.getElementById("loaderWinImg").onload = '';
var newimage=document.getElementById('loaderWin').innerHTML;
document.getElementById('imagewin').innerHTML = newimage;
}
var showImages = setInterval("showNewImage()",15000);
showFirstImage();
</script>
</div>
</body>
</html>
The problem can be seen here (in firefox problem like described above, in chrome there are no pauses between pictureloads and there is a blank window in between picture changes): http://sabine-schneider.silbe.org:1666/test.html
And here, what Rob suggested in answer 1 without any changes (displays the picture fine in firefox, but not in chrome - there I get a blank window in between picture changes): http://sabine-schneider.silbe.org:1666/test0.html
sounds like the image is "progressive" ( interlaced) and the preload needs more time for it to complete download.
You can set a width and height to the image also for a more stable presentation
( poss )
using
?defeat_firefox_caching=" + counter;
means you never cache the image ( which has confused me about your question ) - remove that line( unless you need it for something you haven't mentioned)
update: Can you try ...
<style type="text/css">
#loaderWin { display:block; height:1px; width:1px; overflow:hidden; }
</style>
<div id="imagewin"></div>
<div id="loaderWin"></div>
<script type="text/javascript">
var screenshotCount=0;
function showNewImage() {
screenshotCount = screenshotCount +1;
var newimage=document.getElementById('loaderWin').innerHTML;
document.getElementById('imagewin').innerHTML = newimage;
var nextImage = "<img src='screenshot.png?defeat_firefox_caching="+screenshotCoun+"'/>";
document.getElementById('loaderWin').innerHTML = nextImage;
}
var showImages = setInterval("showNewImage()",5000);
</script>