I have a bunch of hidden images on my website. Their container DIVs have style="display: none". Depending on the user's actions, some images may be revealed via javascript. The problem is that all my images are loaded upon opening the page. I would like to put less strain on the server by only loading images that eventually become visible. I was wondering if there was a pure CSS way to do this. Here are two hacky/complicated ways I am currently doing it. As you can see, it's not clean code.
<div id="hiddenDiv">
<img src="spacer.gif" />
</div>
.reveal .img {
background-image: url(flower.png);
}
$('hiddenDiv').addClassName('reveal');
Here is method 2:
<img id="flower" fakeSrc="flower.png" />
function revealImage(id) {
$('id').writeAttribute(
'src',
$('id').readAttribute('fakeSrc')
);
}
revealImage('flower');
The browser will load any images that has a src attribute set, so what you want to do is to use data-src in the markup and use JavaScript to set the src attribute when you want it to load.
<img class="hidden" data-src="url/to/image.jpg" />
I created this tiny plugin that will take care of the problem:
(function($){
// Bind the function to global jQuery object.
$.fn.reveal = function(){
// Arguments is a variable which is available within all functions
// and returns an object which holds the arguments passed.
// It is not really an array, so by calling Array.prototype
// he is actually casting it into an array.
var args = Array.prototype.slice.call(arguments);
// For each elements that matches the selector:
return this.each(function(){
// this is the dom element, so encapsulate the element with jQuery.
var img = $(this),
src = img.data("src");
// If there is a data-src attribute, set the attribute
// src to make load the image and bind an onload event.
src && img.attr("src", src).load(function(){
// Call the first argument passed (like fadeIn, slideIn, default is 'show').
// This piece is like doing img.fadeIn(1000) but in a more dynamic way.
img[args[0]||"show"].apply(img, args.splice(1));
});
});
}
}(jQuery));
Execute .reveal on the image(s) you want to load/show:
$("img.hidden").reveal("fadeIn", 1000);
See test case on jsFiddle.
Here's a jQuery solution:
$(function () {
$("img").not(":visible").each(function () {
$(this).data("src", this.src);
this.src = "";
});
var reveal = function (selector) {
var img = $(selector);
img[0].src = img.data("src");
}
});
It's similar to your solution, except it doesn't use the fakeSrc attribute in the markup. It clears the src attribute to stop it from loading and stores it elsewhere. Once you are ready to show the image, you use the reveal function much like you do in your solution. I apologize if you do not use jQuery, but the process should be transferable to whichever framework (if any) that you use.
Note: This code specifically must be ran before the window has fired the load event but after the DOM has been loaded.
Weirdly, there's no answer about native lazy loading which is implemented in the majority of the browsers already.
you can do it by adding loading="lazy" attribute to your image.
Addy Osmani wrote a great article about it. You can read more about lazy loading here: https://addyosmani.com/blog/lazy-loading/
It partially depends on how your images must be placed in your code. Are you able to display the images as the background of a <div>, or are you required to use the <img> tag? If you need the <img> tag, you may be screwed depending on the browser being used. Some browsers are smart enough to recognize when an image is inside of a hidden object or in an object of 0 width/height and not load it since it's essentially invisible, anyway. For this reason many people will suggest putting an image in a 1x1 pixel <span> if you want the image to be pre-loaded but not visible.
If you don't require the <img> tag, most browsers won't load images referenced by CSS until the element in question becomes visible.
Mind you that short of using AJAX to download the image there's no way to be 100% sure the browser won't pre-load the image anyway. It's not unbelievable that a browser would want to pre-load anything it assumes may be used later in order to "speed up" the average load times.
Using CSS to put the image an unused class, then adding that class to an element with javascript is going to be your best bet. If you don't use image tags at all, this solution becomes a bit more obvious.
Though, for perspective, most people have the opposite problem where they want to preload an image so it shows up instantly when it's told to be shown.
If you are okay relying on scripting, there is the background image method and the image src method. Put simply, set all your hidden images to some very small image (reduce strain on server) or one that does not exist at all (who cares? The visitor cannot see the image-missing [X] anyway, the div is hidden) then change it with script...
<img src="I_do_not_exist.jpg" id="my_image" />
<input type="button" onclick="document.getElementById('my_image').src='I_exist.jpg';" Value="change image" />
<br /><br /><br />
<div id="mydiv" style="width:40px; height:40px; border:2px solid blue"></div>
<input type="button" onclick="document.getElementById('my_div').style.width='455px';document.getElementById('my_div').style.height='75px';document.getElementById('my_div').style.backgroundImage='url(I_exist.jpg)';" Value="change background image" />
I left a width on the above example to show that nothing is in the div image wise until you ask it to load.
If you make the image a background-image of a div in CSS, when that div is set to 'display: none', the image will not load.
You can do the following for a pure CSS solution, it also makes the img box actually behave like an img box in a responsive design setting (that's what the transparent png is for), which is especially useful if your design uses responsive-dynamically-resizing images.
<img style="display: none; height: auto; width:100%; background-image:
url('img/1078x501_1.jpg'); background-size: cover;" class="center-block
visible-lg-block" src="img/400x186_trans.png" alt="pic 1">
The image will only be loaded when the media query tied to visible-lg-block is triggered and display:none is changed to display:block. The transparent png is used to allow the browser to set appropriate height:width ratios for your <img> block (and thus the background-image) in a fluid design (height: auto; width: 100%).
1078/501 = ~2.15 (large screen)
400/186 = ~2.15 (small screen)
So you end up with something like the following, for 3 different viewports:
<img style="display: none; height: auto; width:100%; background-image: url('img/1078x501_1.jpg'); background-size: cover;" class="center-block visible-lg-block" src="img/400x186_trans.png" alt="pic 1">
<img style="display: none; height: auto; width:100%; background-image: url('img/517x240_1.jpg'); background-size: cover;" class="center-block visible-md-block" src="img/400x186_trans.png" alt="pic 1">
<img style="display: none; height: auto; width:100%; background-image: url('img/400x186_1.jpg'); background-size: cover;" class="center-block visible-sm-block" src="img/400x186_trans.png" alt="pic 1">
And only your default media viewport size images load during the initial load, then afterwards, depending on your viewport, images will dynamically load.
And no javascript!
Related
I have been working on this new website but the "buttons" I am using are causing a ton of lag, I would like to remove the lag if possible.
The website is here: http://lano-project.org/
The troubled code is here:
<td>
<a href="templink-info.html">
<img style="display: none" src="images/icons/hover-info.png"/>
<img src="images/icons/info.png"
onmouseover="this.src='images/icons/hover-info.png';
document.getElementById('home_text').style.display = 'none';
document.getElementById('info_text').style.display = 'block';"
onmouseout="this.src='images/icons/info.png';
document.getElementById('home_text').style.display = 'block';
document.getElementById('info_text').style.display = 'none';"
onclick=""/>
</a>
<h3>Info</h3>
</td>
with relevant css:
#icon tr td img{
width: 100px;
height: 100px;}
#icon tr td p{
margin: 0px;}
#icon tr td{
text-align: center;
width: 150px;}
#icon{
margin-left: auto;
margin-right: auto;}
https://css-tricks.com/snippets/css/basic-link-rollover-as-css-sprite/
You can boost your responsiveness by creating only one image with both states of your button that is twice as large as the button itself. Then, on mouseover, just change the background-position property using css instead of loading a new image every time. This effectively "slides" the image so that the correct part of it shows "through" the button. This operation is very fast, and I think you'll see a big difference.
Compress your images and it the website will load a bit faster.
Post it on jsFiddle and specify the problem, because for now, I don't understand what do you want, because your website loads normally without any lag.
If you want to create an attribute with image, you can change the background of attribute or just use JS for.. some cool stuff you're trying to do.
EDIT: I've done in the past, my solution is to use background-image and change it on :hover
http://puu.sh/qc98m/a828b9ae4e.png like that.
The mouseover is only slow when hovering the images for the first time. What's happening behind the scenes, is that the new (hover) images weren't loaded into the browser's cache when the page loaded, have to be loaded on first mouseover, and hence cause the (visual) delay. Subsequent cursor passes are as fast as would normally be expected.
One possible solution would be to preload images (which would obviously happen in the background) immediately when the page loads. From a similar question:
function preloadImage(url)
{
var img=new Image();
img.src=url;
}
preloadImage('images/icons/hover-info.png');
I'd like to elegantly annotate an image on hover (or tap) with the contents of that image's alt tag. I'd like to use only CSS (if possible), and do not want to use jQuery because the page is incredibly simple and I don't want anything superfluous.
Whilst I can't write JS at the moment, I've only been able to find similar examples that use jQuery: display alt tag for image using this - jquery
Thank you.
Using the HTML5 <figure> element you can use the <figcaption> element to display the description of the image. It's a semantic approach to the thing you want to achieve. The alt attribute is for when the image can not be loaded and be different from the figcaption.
<figure>
<img src="/random-image.jpg" alt="Alt description">
<figcaption>Here comes the description</figcaption>
</figure>
You can hide the figcaption via CSS and on tap (or click) show it.
You can't use :before or :after as #Pointy correctly said. It's a self-closing element, so there is no content. Only attributes.
However, if you can control where the actual alt text goes, you could do something like this. Which makes use of the content/attr CSS property/combo. For details on this see David Walsh's post.
div {
position:relative;
width:500px;
height:300px;
}
div:after {
position:absolute;
bottom:0;
right:0;
padding:10px;
content:attr(data-alt);
color:#FFF;
background:#000;
}
Followed by HTML like this
<div data-alt="Sample Alt Text">
<img src="//placeimg.com/500/300/any" />
</div>
Edit: if you want to page to validate, ensure you add an alt attribute to the img as well. Either empty alt="", providing screen readers are able to read out the CSS content (I'm not 100% sure on this, probably depends on the screen reader). Or add the alt text in the alt attribute as well.
I'm building a site where most of the images go full-bleed, that is, they are width:100%. I am having a problem where the images have a height of 0 until loaded which causes the elements of site to jump around a lot.
I can't leave it blank because it is 0 until after loading.
If I set the height to a pixel value, it doesn't scale correctly.
I can't really set it to 100% because that uses the height of the
containing element.
Is what I'm trying to do possible? I can think of ways to possibly solve this using javascript after the first image loads, but doesn't seem very elegant.
Help!
<img src="http://lorempixel.com/1280/640/cats/1" id="auto" />
<p>This text will get pushed down after load starts</p>
<img src="http://lorempixel.com/1280/640/cats/2" id="fixed" />
<p>This text will get pushed down after load starts</p>
<img src="http://lorempixel.com/1280/640/cats/3" id="percentage" />
<p>This text will get pushed down after load starts</p>
img { width:100%; }
#auto { height: auto; }
#fixed { height: 640px; }
#percentage { height: 100%; }
JSFiddle
The best you could do is have a wrapper div element which is fluid in the sense that it matches image's aspect ratio.
Like below:
HTML
<div class="image-wrapper">
<img src="http://lorempixel.com/1280/640/cats/1" />
</div>
<p>This text will get pushed down after load starts</p>
<div class="image-wrapper">
<img src="http://lorempixel.com/1280/640/cats/2" />
</div>
<p>This text will get pushed down after load starts</p>
<div class="image-wrapper">
<img src="http://lorempixel.com/1280/640/cats/3" />
</div>
<p>This text will get pushed down after load starts</p>
CSS
.image-wrapper {
position:relative;
height:0;
padding-bottom:50%; /* aspect ratio of the image ( ( 640 / 1280 ) * 100% ; ) */
}
.image-wrapper > img {
position:absolute;
top:0;
left:0;
width:100%;
}
Fiddle: http://jsfiddle.net/Varinder/m8dFM/1/
If the browser hasn't downloaded the image yet, and you haven't specified the height of image in the img tag, then it has no way of knowing what height to reserve for it in the page.
The best you might be able to do is set a safe min-height on these images to at least minimise the 'pop' when they load.
There are no solutions, here.
There are potential answers, though.
If you loaded a JSON payload of information about each image, and used document.body.getBoundingClientRect().width, then you could use the body's width (or your container's), and the metadata about the image, to reserve that much height.
Of course, you'd have to load that JSON as one of the first things you did on the site (or bake it into the page, or save it as a JS object, in an included file), and you'd have to save the data for every image you intended to use...
...also, rather than showing nothing, you could then show an SVG (which could either stretch non-uniformly, or could be scaled and letterboxed), which, on-load of your intended image (which would be loaded in JS), you could then set the position of the SVG to absolute (at the same point in the page), and start fading it into the background, while crossfading in your image, which would be appended to the DOM, in the space the SVG was occupying...
Or rather than "pop" in, you could have them all slide in, or zoom in... ...or if you wanted to be really annoying, zoom-out...
It might be overkill for something you consider to be a simple problem... ...and it really probably is.
The sad truth is that there are no solutions, but the happy reality is that you can solve slightly-different problems to make the initial problem seem novel.
I can't wrap my head around this. I have several Divs within a HTML page. Each Div represents a different section and thus contains different images for that section. All the images are referenced from css and displayed/removed using javascript (document.getElementById('DIV').style.display='none/block';).
For the purpose of this example lets say I have 2 divs. Each section Div(Div1 & Div2) would be the parent divs and any Div within those parents will be its child. (DIV1a, DIV2a)
I have found that if Div1 is set using display: block and uses the css Background-image:.... and Div2 is display='none' when I hide Div1 using style.display = 'none'; that it does remove it from the screen and allows me to show Div2..however the background-image is still present in the browser memory.
The interesting thing and which I can't wrap my head around is if I place the background-img into a child div(div1a) within DIV1 when I use style.display = 'none' for the the Parent DIV1 the child div1a image does get removed from the browser memory when I use style.display = 'none' on the parent DIV1. However I have found this also to be inconsistent....it seems to work on some parent divs and not on others.
As you can probably tell by this point I am heavily confused and really don't know how to approach this.
Thank you all for your time and thoughts.
Code Example:
When using this method
<div id="Div1">
content....
</div>
<div id="Div2" style="display: none">
...content
</div>
div#Div1{
background-image: url(images/mybg.png);
background-repeat: no-repeat;
width: 480px;
height: 360px;
}
document.getElementById("Div1").style.display='none';
document.getElementById("Div2").style.display='block';
The image is still present in the resources tab when I execute the above javascript
When using this method:
<div id="Div1">
<div id="Div1a">
content....
</div>
</div>
<div id="Div2" style="display: none">
content....
</div>
div#Div1a{
background-image: url(images/mybg.png);
background-repeat: no-repeat;
width: 480px;
height: 360px;
}
document.getElementById("Div1").style.display='none';
document.getElementById("Div2").style.display='block';
The image gets removed from the resources tab when I execute the above javascript...but this effect is inconsistent and doesn't always work :s
Setting something to display: none does not remove anything from memory in any browser. The entire DOM element is still in the DOM occupying the same amount of memory, it is just marked hidden from view and layout.
If you want to actually remove an element from memory, then you need to physically remove it from the DOM, usually using parent.removeChild(child) AND make sure that there are no references to the DOM element anywhere in your javascript which would keep it from getting garbage collected.
Also, I don't know how you are assessing memory usage in your browser, but most methods will not accurately detect whether a browser has freed a given image or not because the memory may have been freed from an internal pool of memory (available for reuse), but not returned to the OS. Just releasing an image will not necessarily show a reduction in memory usage by the browser. What does show would be highly browser specific and even OS specific and would certainly depend upon exactly what tools you were using to examine memory usage.
I have my current code:
#content img[src="/img/test.gif"] {
background-image:url(dark-img.png) !important;
}
From my understanding !important; overrides existing values?
Why isn't this overriding the current HTML image in place there? The background shows up, behind the HTML image.
I want it in front of the HTML image, is this possible using CSS or JS?
Edit: For what its worth, im making a userscript that will modify the existing style of the site. So I do not have direct access to the HTML image.
You don't need javascript for image replacement! As long as you can identify the image by a CSS selector, you can use CSS to do the trick.
See the solution here
http://www.audenaerde.org/csstricks.html#imagereplacecss
Here is the code using only css:
<img src="tiger.jpg"
style="padding: 150px 200px 0px 0px;
background: url('butterfly.jpg');
background-size:auto;
width:0px;
height: 0px;">
sets the image size to 0x0,
adds a border of the desired size (150x200), and
uses your image as a background-image to fill.
If you upvote this answer, give #RobAu's answer an upvote, too.
The replacement of an image in CSS can be done in several ways.
Each of them has some drawbacks (like semantics, seo, browsercompatibility,...)
On this link 9 (nine!) different techniques are discussed in a very good way :
http://css-tricks.com/css-image-replacement/
If you are interested in css in general : the whole site is worth a look.
The background-image property, when applied to an image, refers to (drum roll ... ) the background-image of the image. It will always be behind the image.
If you want the image to appear in front of the image, you are going to have to use two images, or another container with a background-image that covers the first image.
BTW, it is bad practice to rely on !important for overriding. It can also be ineffective since 1) it can't override declarations in an element's style attribute, and 2) it only works if it can work based on the markup and the current CSS. In your case, all the huffing and puffing and !important declarations won't make an image do something it can't do.
I answered a similar question in another SO page..
https://robau.wordpress.com/2012/04/20/override-image-src-in-css/
<img src="linkToImage.jpg" class="egg">
.egg {
width: 100%;
height: 0;
padding: 0 0 200px 0;
background-image: url(linkToImage.jpg);
background-size: cover;
}
So effectively hiding the image and padding down the background. Oh what a hack but if you want an with alt text and a background that can scale without using Javascript?
Use your 'userscript' to change 'src' attribute value.
If there is an ID there, you can do this:
document.getElementById('TheImgId').src = 'yournewimagesrc';
If there is no ID:
var imgElements = document.getElementsByTagName('img');
Do iteration of imgElements. When its src value is match with your criteria, change the value with your own, do break.
Update:
Javascript:
<script language="javascript">
function ChangeImageSrc(oldSrc, newSrc) {
var imgElements = document.getElementsByTagName('img');
for (i = 0; i < imgElements.length; i++){
if (imgElements[i].src == oldSrc){
imgElements[i].src = newSrc;
break;
}
}
}
</script>
HTML:
<img src="http://i.stack.imgur.com/eu757.png" />
<img src="http://i.stack.imgur.com/IPB9t.png" />
<img src="http://i.stack.imgur.com/IPB9t.png" />
<script language="javascript">
setTimeout("ChangeImageSrc('http://i.stack.imgur.com/eu757.png', 'http://i.stack.imgur.com/IPB9t.png')", 5000);
</script>
Preview:
The first image will be replaced after 5 secs. Try Live Demo.
you'll have to place the first image as a background-image too. Then you can override it. You could do in a "standard" css file for the site, and every user gets its own, where he can override what he wants.
i agree with all the answers here, just thought id point out that 'browsers' such as IE won't like the img[src="/img/test.gif"] as a means of selecting the image. it would need a class or id.
The images shown in tags are in the foreground of the element, not the background, so setting a background image in an won't override the image; it'll just appear behind the main image, as you're seeing.
What you want to do is replace the image. Here's your options:
Start with an element with a background image, not an tag. Then changing the background image in CSS will replace it.
Start with an tag, but use Javascript to change the src attribute. (this can't be done in CSS, but is simple enough in JS)
EDIT:
Seeing your edit in the question, I'd suggest option 2 - use Javascript to change the src attribute. It's quite simple; something like this would do the trick:
document.getElementById('myimgelement').src='/newgraphic.jpg';
You should be able to replace it by just doing something like:
.image {
content: url('https://picsum.photos/seed/picsum/400');
width: 200px;
height: 200px;
}
Unfortunately seems that it does not work in Firefox :(