I have a few images on my page. I'm finding that the page starts to render before the images have been loading (which is good), but that the visual effect is not great. Initially the user sees this:
--------hr--------
text
Then a few milliseconds later the page jumps to show this:
--------hr--------
[ ]
[ image ]
[ ]
text
Is there a simple way that I can show a grey background image of exactly the width and height that the image will occupy, until the image itself loads?
The complicating factor is that I don't know the height and width of the images in advance: they are responsive, and just set to width: 100% of the containing div. This is the HTML/CSS:
<div class="thumbnail">
<img src="myimage.jpeg" />
<div class="caption">caption</div>
</div>
img { width: 100% }
Here's a JSFiddle to illustrate the basic problem: http://jsfiddle.net/X8rTB/3/
I've looked into things like LazyLoad, but I can't help feeling there must be a simpler, non-JS answer. Or is the fact that I don't know the height of the image in advance an insurmountable problem? I do know the aspect ratio of the images.
Instead of referencing the image directly, stick it within a DIV, like the following:
<div class="placeholder">
<div class="myimage" style="background-image: url({somedynamicimageurl})"><img /></div>
</div>
Then in your CSS:
.placeholder {
width: 300;
height: 300;
background-repeat: no-repeat;
background-size: contain;
background-image: url('my_placeholder.png');
}
Keep in mind - the previous answers that recommend using a div background approach will change the semantic of your image by turning it from an img into a div background. This will result in things like no indexing of these images by a search crawler, delay in loading of these images by the browser (unless you explicitly preload them), etc.
A solution to this issue (while not using the div background approach) is to have a wrapper div to your image and add padding-top to it based on the aspect ratio of the image that you need to know in advance. The below code will work for an image with an aspect ratio of 2:1 (height is 50% of width).
<div style="width:100%;height:0; padding-top:50%;position:relative;">
<img src="<imgUrl>" style="position:absolute; top:0; left:0; width:100%;">
</div>
Of course - the major disadvantage of this approach is that you need to know the aspect ratio of the image in advance.
There is a really simple thing to check before you start looking into lazy-loading and other JavaScript. Make sure the JPEG images you are loading are saved with the 'progressive' option enabled!
This will cause them to load the image iteratively, starting with a placeholder that is low-resolution and faster to download, rather than waiting for the highest resolution data before rendering.
It's very simple...
This scenario allows to load a profile photo that defaults to a placeholder image.
You could load multi CSS background-image into an element. When an avatar photo fails, the placeholder image appears default of div.
If you're using a div element that loads via a CSS background-image, you could use this style:
#avatarImage {
background-image: url("place-holder-image.png"), url("avatar-image.png");
}
<div id="avatarImage"></div>
Feel free to copy this:
<script>
window.addEventListener("load", function () {
document.getElementById('image').style.backgroundColor = 'transparent';
});
</script>
<body>
<image src="example.example.example" alt="example" id="image" style="background-color:blue;">
</body>
I got this from here: Preloader keeps on loading and doesnt disappear when the content is loaded.
Apart from all solutions already mentioned, the last solution would be to hide the document until everything is loaded.
window.addEventListener('load', (e) => {
document.body.classList.add('loaded');
});
body {
opacity: 0;
}
body.loaded {
opacity: 1;
}
<div id="sidebar">
<img src="http://farm9.staticflickr.com/8075/8449869813_1e62a60f01_b.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-1.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-2.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-3.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-4.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-5.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-6.jpg" />
</div>
Or show some animation while everything is loading:
window.addEventListener('load', (e) => {
document.body.classList.add('loaded');
});
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 70px;
height: 70px;
-webkit-animation: spin 2s linear infinite;
/* Safari */
animation: spin 2s linear infinite;
position: absolute;
left: calc(50% - 35px);
top: calc(50% - 35px);
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
body :not(.loader) {
opacity: 0;
}
body .loader {
display: block;
}
body.loaded :not(.loader) {
opacity: 1;
}
body.loaded .loader {
display: none;
}
<div class="loader"></div>
<div id="sidebar">
<img src="http://farm9.staticflickr.com/8075/8449869813_1e62a60f01_b.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-1.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-2.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-3.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-4.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-5.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-6.jpg" />
</div>
The only thing I can think of, to minimize the jump effect on your text, is to set min-height to where the image will appear, I would say - set it to the "shorter" image you know of. This way the jump will be less evident and you won't need to use lazyLoad or so... However it doesn't completely fix your problem.
Here's one naive way of doing it,
img {
box-shadow: 0 0 10px 0 rgba(#000, 0.1);
}
You can manipulate the values, but it creates a very light border around the image that doesn't push the contents. Images can load at whatever time they want, and you get a good user experience.
Here is what I did with Tailwind CSS, but it's just CSS:
img {
#apply bg-no-repeat bg-center;
body.locale-en & {
background-image: url("data:image/svg+xml;utf8,<svg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><text x='50%' y='50%' style='font-family: sans-serif; font-size: 12px;' dominant-baseline='middle' text-anchor='middle'>Loading…</text></svg>");
}
body.locale-fr & {
background-image: url("data:image/svg+xml;utf8,<svg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><text x='50%' y='50%' style='font-family: sans-serif; font-size: 12px;' dominant-baseline='middle' text-anchor='middle'>Chargement…</text></svg>");
}
}
You can find the width and height of the images in the developer tools console, for example in Chrome you can click the cursor icon in the developer tools console and when you hover on the page it will highlight all the properties of the elements in the page.
This will help you find the width and height of the images, because if you hover on top of your images it will give you the dimensions of the image and other more properties. You can also make an individual div for each image and make the div relative to the images width and height. You can do it like this:
The main div will contain the images and also the background-div which is below the image.
HTML:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div class=".mainDiv">
<div class="below"></div>
<img src="https://imgix.bustle.com/uploads/image/2020/2/13/da1a1ca4-95ec-40ea-83c1-4f07fac8b9b7-eqb9xdwx0auhotc.jpg" width="500"/>
</div>
</body>
</html>
CSS:
.mainDiv {
position: relative;
}
.below {
position: absolute;
background: #96a0aa;
width: 500px;
height: 281px;
}
img {
position: absolute;
}
The result will be that .below will be below the image and so when the image has trouble loading the user will instead see the grey .below div. You cannot see the .below div because it is hidden below the image. The only time you will see this is when the loading of the image is delayed. And this will solve all your problems.
I have got a way. But you will need to use JavaScript for it.
The HTML:
img = document.getElementById("img")
text = document.getElementById("text")
document.addEventListener("DOMContentLoaded", () => {
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAA1BMVEWIiIhYZW6zAAAASElEQVR4nO3BgQAAAADDoPlTX+AIVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwDcaiAAFXD1ujAAAAAElFTkSuQmCC";
text.innerHTML = "Loaded but image is not";
});
window.onload = function() {
img.src = "https://media.geeksforgeeks.org/wp-content/uploads/20190913002133/body-onload-console.png";
text.innerHTML = "Image is now loaded";
};
#img {
width: 400px;
height: 300px;
}
<hr>
<img id="img" src="https://media.geeksforgeeks.org/wp-content/uploads/20190913002133/body-onload-console.png">
<p>Here is the Image</p>
<p id="text">Not Loaded</p>
Related
I have an image, and on top of that image is another smaller image. When the first image is clicked, the second one appears, and it disappears when the first image is clicked again. My problem is when the second image appears over the first one, it makes the area that it covers of the first image unclickable. Is it possible to make it so the first image can be clicked through the second image?
This is the HTML for the second image (it's generated in PHP):
Echo '<img src="images/tick.png" id="tick' . $i .'" class="hidden" style="position: absolute; top: 0px; left: 70%;"/>';
Simply put both images in a container div, and attach the click event handler to that instead of the bigger image. This way you can simply make use of event bubbling (which isn't available on the bigger image since it cannot have child elements, such as the smaller image).
Find a working solution here:
https://jsfiddle.net/6nnwy3xw/
$(document).ready(function() {
$('.imgcontainer').on('click', function() {
$(this).toggleClass('toggleImg');
});
})
.imgcontainer {
height: 300px;
width: 300px;
position: relative;
}
.imgcontainer img:first-child {
display: block;
height: 300px;
width: 300px;
}
.imgcontainer img+img {
position: absolute;
top: 50px;
left: 50px;
opacity: 0;
transition-duration: 0.3s;
}
.imgcontainer.toggleImg img+img {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="imgcontainer">
<img src="http://lorempixel.com/300/300" />
<img src="http://lorempixel.com/200/200" />
</div>
I'm assuming your use-case is some kind of checkbox replacement element? In this case, this may also be of interest to you:
Use images like checkboxes
If that is the case, I'd make the surrounding diva label instead, so it also automatically checks your (probably hidden) real checkbox.
If I understand the issue you're describing properly, you could try turning pointer-events off for the second image, that is often displayed over the click-target:
.two { pointer-events: none; }
Note that this is only supported with HTML in Internet Explorer 11 and up (as well as in Chrome and Firefox). For SVG, support was available in IE 9. That may suffice for a work-around if needed.
Fiddle: http://jsfiddle.net/tbqxjp19/
For better support you should move your handler to an element that will not be obstructed, and as such will always work to toggle the visibility of the second image:
<div class="images">
<img src="http://placehold.it/100x100" class="one" />
<img src="http://placehold.it/100x100/000000" class="two" />
</div>
document.querySelector( ".images" ).addEventListener( "click", function () {
this.classList.toggle( "toggled" );
});
The above simply binds a handler to click events on the .images container, toggling a class that will hide and/or reveal the second image, given the following:
.toggled .two {
opacity: .1;
}
Fiddle: http://jsfiddle.net/tbqxjp19/1/
Try this , if you are fine with jquery solution.
HTML
<img src="images/large.png" class ="image" id="image1" style="position: absolute; top: 0px; left: 0px;" />
<img src="images/small.png" id="image2" class ="image" style="position: absolute; top: 0px; left: 0px; z-index:10;" />
css
.hiddenimage{
display:none;
}
JQuery
$(".image").click(function(){
("#image2").toggleClass("hiddenimage");
})
I would like to hover over div id-"RollOver1" and be able to change the background to a different image from the main one. Only pasted the HTML for the rollover div cant use jscript so is there a way in HTML or ....?
<div id="RollOver1" style="position:absolute;overflow:hidden;left:152px;top:397px;width:183px;height:183px;z-index:4">
<a href="./car.html">
<img class="hover" alt="" src="images/Enter_02.jpg" style="left: 0px; top: 0px; width: 183px; height: 183px; display: block;">
<span style="display: none;"><img alt="" src="images/index_01.jpg" style="left:0px;top:0px;width:183px;height:183px"></span>
</a>
</div>
You can do this with the following code:
#RollOver1 {
background:url(INITIAL_BACKGROUND);//here use the url of the background you want when is NOT on hover
}
#RollOver1:hover {
background:url(BACKGROUND_ON_HOVER);//here use the url of the bg you want when is on hover
}
You can use :hover pseudo class:
#RollOver1 {
background: url('img1.png');
}
#RollOver1:hover {
background: url('img2.png');
}
But you will usually see "glich" between changes of images, because second image will take some time to be loaded.
To avoid that, use image sprite. Put both images (normal and hover) to single image and than use css background-position
#RollOver1 {
background: url('sprite.png') no-repeat 0 0;
}
#RollOver1:hover {
background-position: -80px -90px;
}
It will be more efficient way to load small images (like buttons, icons and so on).
Check this link
Using JQuery you can try
$(document).on("mouseover", "#RollOver1", function(e) {
$(this).css("background", "url(sampleImage.png) no-repeat");
}
});
use the css pseudo class :hover
You can use below styles
.RollOver1:hover {
background-image: url('paper.gif');
}
I am trying to setup background images using CSS but I can't seem to get the images to populate correctly.
Here is the CSS for what I want to do
a.fb {
background-image: url('img/Facebook.png');
}
a.fb:hover {
background-image: url('img/FacebookHover.png');
}
Here is the html code that I am using, I have tried a couple of different ways to populate the images with no luck
<div class="footer">
<a class="fb" href="http://www.facebook.com" target="_blank"></a>
</div>
Any help would be greatly appreciated
Okay added the following and still not go any other thoughts
a.fb {
display:block;
width: 33px;
height: 33px
background-image: url('img/Facebook.png');
}
EDIT: Yup got it working now forgot the ; after height, but no I get a white border around it and tried setting border: none; no luck
a.fb {
border: none;
display:block;
width: 33px;
height: 33px;
background-image: url('img/Facebook.png');
}
An anchor tag by default shows as an inline elements, so it depends on its content in order to get a height and width. To do what you want, you should add some styles: display:block; width: 20px; height: 20px.
You could also change the aproach completely and use html + mouseover and mouseout events:
<div class="footer">
<a class="fb" href="http://www.facebook.com" target="_blank">
<img src="http://www.facebook.com/images/fb_icon_325x325.png" alt="fb" name="fb" width="33px" height="33px" name="image_name" onmouseover="fb.src='http://goo.gl/cxiR7'; fb.width='38'; fb.height='38';" onmouseout="fb.src='http://www.facebook.com/images/fb_icon_325x325.png'; fb.width='33'; fb.height='33';" />
</a>
</div>
Here is a jsBin: http://jsbin.com/onehuw/1/edit
background-image only draws in the space that the element occupies. Your a tag has no content, and therefore it's width is 0. You'll not see any content (and background) until you give it at least some width (and height if needed).
You need to add padding to the <a> tag otherwise it has a width and height of 0 for example:
a.fb {
padding: 20px;
background-image: url('img/Facebook.png');
}
a.fb:hover {
background-image: url('img/FacebookHover.png');
}
You could also just set the width and height of the anchor
When I use image tags in html, I try to specify its width and height in the img tag, so that the browser will reserve the space for them even before the images are loaded, so when they finish loading, the page does not reflow (the elements do not move around). For example:
<img width="600" height="400" src="..."/>
The problem is now I want to create a more "responsive" version, where for the "single column case" I'd like to do this:
<img style="max-width: 100%" src="..."/>
but, if I mix this with explicitly specified width and height, like:
<img style="max-width: 100%" width="600" height="400" src="..."/>
and the image is wider than the available space, then the image is resized ignoring the aspect ratio. I understand why this happens (because I "fixed" the height of the image), and I would like to fix this, but I have no idea how.
To summarize: I want to be able to specify max-width: 100%, and also somehow make sure the content is not reflowed when the images are loaded.
UPDATE 2: (Dec 2019)
Firefox and Chrome now deal with this by default. Simply add the width and height attributes as normal. See this blog post for more details.
UPDATE 1: (July 2018)
I found a much cleverer alternate version of this: http://cssmojo.com/aspect-ratio-using-custom-properties-and-calc/. This still requires a wrapper element and it requires CSS custom properties, but I think it's much more elegant. Codepen example is here (credit to Chris Coyier's original).
ORIGINAL:
From this blog post by Jonathan Hollin: add the image's height and width as part of an inline style. This reserves space for the image, preventing reflow when the image loads, but it's also responsive.
HTML
<figure style="padding-bottom: calc((400/600)*100%)">
<img src="/images/kitten.jpg" />
</figure>
CSS
figure {
position: relative;
}
img {
max-width: 100%;
position: absolute;
}
The figure can be replaced with a div or any other container of your choice. This solution relies on CSS calc() which has pretty wide browser support.
Working Codepen can be seen here.
I'm also looking for the answer to this problem. With max-width, width= and height=, the browser has enough data that it should be able to leave the right amount of space for an image but it just doesn't seem to work that way.
I worked around this with a jQuery solution for now. It requires you to provide the width= and height= for your <img> tags.
CSS:
img { max-width: 100%; height: auto; }
HTML:
<img src="image.png" width="400" height="300" />
jQuery:
$('img').each(function() {
var aspect_ratio = $(this).attr('height') / $(this).attr('width') * 100;
$(this).wrap('<div style="padding-bottom: ' + aspect_ratio + '%">');
});
This automatically applies the technique seen on: http://andmag.se/2012/10/responsive-images-how-to-prevent-reflow/
At first I would like to write about the answer from october 2013. This was incomplete copied and because of them it is not correct. Do not use it. Why? We can see it in this snippet (scroll the executed snippet to the bottom):
$('img').each(function() {
var aspect_ratio = $(this).attr('height') / $(this).attr('width') * 100;
$(this).wrap('<div style="padding-bottom: ' + aspect_ratio + '%">');
});
img { max-width: 100%; height: auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width:300px;border:1px solid red">
<img width="400" height="300" src="data:image/png;base64,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"/>
Some text
</div>
And we can see the text is afar from bottom. What is in this example incomplete/ incorrect? I will show it with correct example with pure JavaScript (we do not need to download jQuery for that).
Correct example with pure JavaScript
Please scroll the executed snippet to the bottom.
var imgs = document.querySelectorAll('img');
for(var i = 0; i < imgs.length; i++)
{
var aspectRatio = imgs[i].getAttribute('height') /
imgs[i].getAttribute('width') * 100;
var div = document.createElement('div');
div.style.paddingBottom = aspectRatio + '%';
imgs[i].parentNode.insertBefore(div, imgs[i]);
div.appendChild(imgs[i]);
}
.restrict-container div{position:relative}
img
{
position:absolute;
max-width:100%;
top:0; left:0;
height:auto
}
<div class="restrict-container" style="width:300px;border:1px solid red">
<img width="400" height="300" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAEsCAIAAABi1XKVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAf3SURBVHhe7do9chtXGoVhb2Ryr0TllSjwLpx7CY6cusrZBFqAEydegPMpL2NEUhTvPf0HkBBxDDxVT6BXajbB7r5fNyB+992v/wD8O2QD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxsu5ref/vef/z775Y8/FxvAmbLhYgwsLi377n3/x4evayxW2g9/PeRPn6bt2WZgcWnZd2w5qgYfPn76+ZenP//98/eLrwXeQ/a92p1W4ccfFl8OvIfs+/Tpx3EkxZuXp3eCAwMLriT7Lo0jaf0jqmmiGVhwJdn36M+Pfz8Po83Pp4ZtDgfW78+fdr348PH3xWYrz26fx+X4jZ5fT+zwhA/Rlnveeg2XNn3QPtl+2d/kOMxPzYNzjsPqTv76bf3HfPj7xR6ueS5uUfY9OmkYvVx22+vk8IOw+fFtXpPn2brit+fFs2/7v3WbY2LnuF34OJz2ceTRyNj5QbbkD3jtc3GTsu/SdA/cuE9+vb1v/FrDyatuuKzX7r0nW67/09fY9sx9s8s8YZ3h9cdh8+Z0zv/ADMYrp+Jc3KLs+7TyJu68u18sufjaXAAxE+Pi/vKvOQGfBuWwq/kZIVdIrsYcCltz+cKGn+JwZV7kOBw5vjmtv4wna7el5U5Kz8VNyL5bazNrsPu51XSBbm05PXpMj2njlw/X7nhZv2w/bDzsZNr55qidf8bpNXwrrx1YrzwOp9h/Sdun6dnB7af3XNyE7PuWt76FtQtrvOvu3u3Ha3RcKhtrb3gxwxBc3Xhjqa8Yt3yPNyOvHFivPA6nWd/ho3EYbT9iz89ZccB7z8VNyOZR3kUHeR2PY2j/Ap2WyjDa3rxQhy3PsvUweEHXHFg7J/FZHoGdWTYZZ8180ovPxU3IZkV8kLz54dHxrX5147cu1PmGf4bzPv15lesMrJOnRoyJk1/t5l2q+VzchGw2bL5Z2F05aXXjty5UA+tl48XdZZ+B9W+TfYeeL77tzyyeDCthvEaHlXO0h2nqXW5gTQ8U8+q9unceWDkvVs/I+g6P/mkyfPedt4Rl5+ImZN+fU6/RrYE13dJP3sPx2jtjoU4f1hzOhXf1vgNrfPDZPhc7Z3zzOXoyj8V5YBWfi5uQfX/GW+LOI9LOZtM1urlOprcq0x7evlDnnccSSl/3sL/ZZbzvwNrYw+joZE1HcnUn8x6Wh7H2XNyE7PszTqIneZlO9+3Plh83HLwTyUs8lu7bF+r894+WL3JeSOvbXNzVBtZiGOU5WtvmURzJaZSs7WQ5a0rPxU3Ivj/LgbVv4ylseQlueVkkW9/6aQUu/vXxC3MxvCzXHIvH1pbrG5z/Ar4czAseh3PP5hfzMD3vB1l7OLr6ubhZ2ffn6yX+efEcXe4Ht8HD1TIPu9V7/qPHNbDc28OaXCzUacHkw+COi6+Q7R9nx8OkuPBx2D8LG2d5cWaX+9+yNrAeXPNc3K7sO/Tlwnq5aNZuj+dcUmvX+tfnoNHW0nrFk8XsYAR8Iw1PWM+WR+BlJK18u623qyun8mk/wwP1wSG9zrm4WdnAsdMHFheVDRwzsK4kGzgyvvldfbPPt5IN7Jo+qjv8XQ0uKxt4tPVfAQO/PPXusoEHh79YZ1pdQzbwaOe3NHxudTXZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgFqZQPUygaolQ1QKxugVjZArWyAWtkAtbIBamUD1MoGqJUNUCsboFY2QK1sgFrZALWyAWplA9TKBqiVDVArG6BWNkCtbIBa2QC1sgE6/frP/wEiNOVSBSWikgAAAABJRU5ErkJggg=="/>
Some text<br>
<img width="400" height="300" src="data:image/png;base64,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"/>
Some text
</div>
The mistake from answer from october 2013: the image should be placed absolute (position:absolute) to the wrapped container but it is not so placed.
This is the end of my answer to this question.
For further information read more about:
What could we do with the new HTML5 technology for responsive images too? (this is previous extended version of my posting here. See there the second part).
For a css only solution, you can wrap the img in a container where the padding-bottom percentage reserves space on the page until the image loads, preventing reflow.
Unfortunately, this approach does require you to include the image aspect ratio in your css (but no need for inline styles) by calculating (or letting css calculate for you) the padding-bottom percentage based on the image height and width.
If many of your images can be grouped into a few standard aspect ratios, then you could create a class for each aspect ratio to apply the appropriate padding-bottom percentage to all images with that aspect ratio. This may save you a little time and effort if you are not dealing with a wide variety of image aspect ratios.
Following is some example html and css for an image with a 2:1 aspect ratio:
HTML
<div class="container">
<img id="image" src="https://via.placeholder.com/300x150" />
</div>
CSS
.container {
display: block;
position: relative;
padding-bottom: 50%; /* calc(100%/(300/150)); */
height: 0;
}
.container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
The snippet below adds some extra html, css and javascript to create some visual top and bottom reference points and mimic a very slow loading image so you can visually see how the reflow is prevented with this approach.
const image = document.getElementById('image');
const source = 'https://via.placeholder.com/300x150';
const changeSource = () => image.src = source;
setTimeout(changeSource, 3000);
.container {
display: block;
position: relative;
padding-bottom: 50%; /* calc(100%/(300/150)); */
height: 0;
}
.container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.top, .bottom {
background-color: green;
width: 100%;
height: 20px;
}
<div class="top"></div>
<div class="container">
<img id="image" src="" />
</div>
<div class="bottom"></div>
If I understand the requirements ok, you want to be able to set an image size, where this size is known only on content (HTML) generation, so it can be set as inline styles.
But this has to be independent of the CSS, and also prior to image loading, so also independent from this image sizes.
I have come to a solution tha involves wrapping the image in a div, and including in this div an svg that can be set to have proportions directly as an inline style.
Obviously this is not much semantic, but at least it works
The containing div has a class named img to show that it , well, should be an img
To try to reproduce the loading stage, the images have a broken src
.container {
margin: 10px;
border: solid 1px black;
width: 200px;
height: 400px;
position: relative;
}
.img {
border: solid 1px red;
width: fit-content;
max-width: 100%;
position: relative;
}
svg {
max-width: 100%;
background-color: lightgreen;
opacity: 0.1;
}
#ct2 {
width: 500px;
}
.img img {
position: absolute;
width: 100%;
height: 100%;
max-height: 100%;
max-width: 100%;
top: 0px;
left: 0px;
box-shadow: inset 0px 0px 10px blue;
}
<div class="container" id="ct1">
<div class="img">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 400 300" width="400">
</svg>
<img width="400" height="300" src="missing.jpg">
</div>
</div>
<div class="container" id="ct2">
<div class="img">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 40 30" width="400">
</svg>
<img width="400" height="300" src="missing.jpg">
</div>
</div>
I find the best solution is to create a transparent base64 gif with corresponding dimensions as a placeholder for img tags where loading is triggered via js after page is loaded.
<img data-src="/image.png" src="data:image/gif;base64,R0lGODlhyAAsAYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
For blog posts and such I use this PHP function to create them automatically
function CreatePreloadPlaceholderGif($width, $height) {
$wHex = str_split(str_pad(dechex($width), 4, "0", STR_PAD_LEFT), 2);
$hHex = str_split(str_pad(dechex($height), 4, "0", STR_PAD_LEFT), 2);
$hex = "474946383961".$wHex[1].$wHex[0].$hHex[1].$hHex[0]."800100ffffff00000021f904010a0001002c00000000010001000002024c01003b";
$base64= '';
foreach(str_split($hex, 2) as $pair){
$base64.= chr(hexdec($pair));
}
return base64_encode($base64);
}
echo CreatePreloadPlaceholderGif(300, 500);
// R0lGODlhLAH0AYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
On the frontend the result is something like this
function loadimage() {
elements = document.querySelectorAll('img[data-src]');
elements.forEach( el => {
el.setAttribute('src', el.getAttribute('data-src'))
});
}
img {
background-color:#696969;
}
<div>300x500 image placeholder</div>
<img data-src="https://ibec.or.id/wp-content/uploads/2018/10/300x500.png" src="data:image/gif;base64,R0lGODlhLAH0AYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
<div>After page load, run js command to replace src attribute with data-src</div>
<button onclick="loadimage()">Load image</button>
I would like the top half of this image to display by default, and then use some CSS to make the image shift upward so that the bottom half shows when the mouse hovers over it. Here is the code and what I've tried, but it is not working. Can anyone help me make this code work?
HTML:
<div id="next">
<img src="images/next3.png" alt="next page">
</div>
CSS:
#next a:hover{background: url('images/next3.png') 0 -45px;}
EDIT:
HTML:
<div id="next">
</div>
CSS:
#next {
height:40px;
width:160px;
background-image:url('images/next3.png');
}
#next:hover{background-position: 100% 100%;}
I think you need to use background-position attribute to achieve this.
CSS
div
{
height:40px;
width:160px;
background-image:url('http://i.stack.imgur.com/OOGtn.png');
}
div:hover
{
background-position:100% 100%;
}
JS Fiddle Example
You can also look into CSS Sprites.
You need to use it as a background in the first place. The <img> is covering the background.
Get rid of the image HTML and just use some CSS like this
a {
display: inline-block;
height: 40px;
width: 160px;
background: transparent url(img.jpg) 0 0 no-repeat;
}
a:hover {
background-position: 0 40px;
}
In this case you will need to remove your <img> tag and consistently use the CSS background attribute for both cases. Also define your height and width width of your a tag with CSS too.