is there an easy way to get the final height and width of a background image with Javascript or jQuery even if a background-size property was applied?
I mean, I know I can get the background image url and load it to an Image object and then get the width and height. But it is the size of the source image. If someone scaled it with CSS then the size changed
How can I find its final size?
#edit
it is different from the question marked as similar because it doesnt say how to get the size in pixels if someone changed the background-size
Using getComputedStyle, I've created this script that returns the width and height of a given element's background, in pixels. It works with:
Dimensions (width or height) set to auto, either explicitly or because no specific value was given (width and height default to auto)
Dimensions set to percentage %
Dimensions set to pixels px
Dimensions set to a combination of any of the previous. (i.e width: 100px; height: auto or width: auto; height: 32.4% or height: 100px; width: 2% or width: 21.2%)
background-size set to cover or contain
It works if background-size is set with an external CSS file, inline CSS, inline header CSS or if it is not set at all (meaning width and height are auto).
Here's a JsFiddle (with cover example)
http://jsfiddle.net/gp4e9d3z/3/
And here's StackOverflow's code snippet (with percentage auto units)
function getBackgroundSize(elem) {
// This:
// * Gets elem computed styles:
// - CSS background-size
// - element's width and height
// * Extracts background URL
var computedStyle = getComputedStyle(elem),
image = new Image(),
src = computedStyle.backgroundImage.replace(/url\((['"])?(.*?)\1\)/gi, '$2'),
cssSize = computedStyle.backgroundSize,
elemW = parseInt(computedStyle.width.replace('px', ''), 10),
elemH = parseInt(computedStyle.height.replace('px', ''), 10),
elemDim = [elemW, elemH],
computedDim = [],
ratio;
// Load the image with the extracted URL.
// Should be in cache already.
image.src = src;
// Determine the 'ratio'
ratio = image.width > image.height ? image.width / image.height : image.height / image.width;
// Split background-size properties into array
cssSize = cssSize.split(' ');
// First property is width. It is always set to something.
computedDim[0] = cssSize[0];
// If height not set, set it to auto
computedDim[1] = cssSize.length > 1 ? cssSize[1] : 'auto';
if(cssSize[0] === 'cover') {
// Width is greater than height
if(elemDim[0] > elemDim[1]) {
// Elem's ratio greater than or equal to img ratio
if(elemDim[0] / elemDim[1] >= ratio) {
computedDim[0] = elemDim[0];
computedDim[1] = 'auto';
} else {
computedDim[0] = 'auto';
computedDim[1] = elemDim[1];
}
} else {
computedDim[0] = 'auto';
computedDim[1] = elemDim[1];
}
} else if(cssSize[0] === 'contain') {
// Width is less than height
if(elemDim[0] < elemDim[1]) {
computedDim[0] = elemDim[0];
computedDim[1] = 'auto';
} else {
// elem's ratio is greater than or equal to img ratio
if(elemDim[0] / elemDim[1] >= ratio) {
computedDim[0] = 'auto';
computedDim[1] = elemDim[1];
} else {
computedDim[1] = 'auto';
computedDim[0] = elemDim[0];
}
}
} else {
// If not 'cover' or 'contain', loop through the values
for(var i = cssSize.length; i--;) {
// Check if values are in pixels or in percentage
if (cssSize[i].indexOf('px') > -1) {
// If in pixels, just remove the 'px' to get the value
computedDim[i] = cssSize[i].replace('px', '');
} else if (cssSize[i].indexOf('%') > -1) {
// If percentage, get percentage of elem's dimension
// and assign it to the computed dimension
computedDim[i] = elemDim[i] * (cssSize[i].replace('%', '') / 100);
}
}
}
// If both values are set to auto, return image's
// original width and height
if(computedDim[0] === 'auto' && computedDim[1] === 'auto') {
computedDim[0] = image.width;
computedDim[1] = image.height;
} else {
// Depending on whether width or height is auto,
// calculate the value in pixels of auto.
// ratio in here is just getting proportions.
ratio = computedDim[0] === 'auto' ? image.height / computedDim[1] : image.width / computedDim[0];
computedDim[0] = computedDim[0] === 'auto' ? image.width / ratio : computedDim[0];
computedDim[1] = computedDim[1] === 'auto' ? image.height / ratio : computedDim[1];
}
// Finally, return an object with the width and height of the
// background image.
return {
width: computedDim[0],
height: computedDim[1]
};
}
// Stuff for debugging
function updateData() {
var background = getBackgroundSize(document.body);
document.getElementById('width').innerHTML = background.width + 'px';
document.getElementById('height').innerHTML = background.height + 'px';
document.getElementById('winWidth').innerHTML = getComputedStyle(document.body).width;
document.getElementById('winHeight').innerHTML = getComputedStyle(document.body).height;
}
// Execute onload, so that the background image is already loaded.
window.onload = window.onresize = updateData;
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
background: url('http://hdwallpapersfit.com/wp-content/uploads/2015/03/images-7.jpg');
background-size: 80% auto;
}
div {
background: rgba(0, 0, 0, 0.5);
color: #fff;
}
<div id="data">
Background width: <span id="width"></span>
<br>
Background height: <span id="height"></span>
<hr>
Body width: <span id="winWidth"></span>
<br>
Body height: <span id="winHeight"></span>
</div>
Using the JSFiddle Here, I found that changing the height or width of the container forces the image to be scaled to the largest height or width. Meaning that the measurement of one edge of the background will be equal to one of the dimension of the container. Using this and some proportions we can calculate the dimensions of the image.
// let .container represent element containing the image
var image; // the image object to the background image
var dim_h, dim_w; // the height and width of the actual image
height = $(".container").height();
width = $(".container").width();
if (height >= width)
{
dim_h = height;
dim_w = (height / image.height) * image.width;
}
else
{
dim_w = width;
dim_h = (width / image.width) * image.height;
}
// dim_w and dim_h contain the width and height of the actual
// background image after scaling
The above code uses the proportion below to calculate it.
(element_height / image_height) == (element_width / image_width)
I think it should give you the answer you want.
Related
could you please tell me why I am getting background image 0 in safari . I am getting 225px width in chrome and firefox and zero in safari
here is my code
https://codesandbox.io/s/lucid-spence-cuyf7?file=/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
<style>
.img {
background: url("./download.jpeg") no-repeat left;
}
</style>
</head>
<body>
<div class="img" alt="" />
<button onclick="abc()">click</button>
<script>
function getBackgroundSize(elem) {
// This:
// * Gets elem computed styles:
// - CSS background-size
// - element's width and height
// * Extracts background URL
var computedStyle = getComputedStyle(elem),
image = new Image(),
src = computedStyle.backgroundImage.replace(
/url\((['"])?(.*?)\1\)/gi,
"$2"
),
cssSize = computedStyle.backgroundSize,
elemW = parseInt(computedStyle.width.replace("px", ""), 10),
elemH = parseInt(computedStyle.height.replace("px", ""), 10),
elemDim = [elemW, elemH],
computedDim = [],
ratio;
// Load the image with the extracted URL.
// Should be in cache already.
image.src = src;
// Determine the 'ratio'
ratio =
image.width > image.height
? image.width / image.height
: image.height / image.width;
// Split background-size properties into array
cssSize = cssSize.split(" ");
// First property is width. It is always set to something.
computedDim[0] = cssSize[0];
// If height not set, set it to auto
computedDim[1] = cssSize.length > 1 ? cssSize[1] : "auto";
if (cssSize[0] === "cover") {
// Width is greater than height
if (elemDim[0] > elemDim[1]) {
// Elem's ratio greater than or equal to img ratio
if (elemDim[0] / elemDim[1] >= ratio) {
computedDim[0] = elemDim[0];
computedDim[1] = "auto";
} else {
computedDim[0] = "auto";
computedDim[1] = elemDim[1];
}
} else {
computedDim[0] = "auto";
computedDim[1] = elemDim[1];
}
} else if (cssSize[0] === "contain") {
// Width is less than height
if (elemDim[0] < elemDim[1]) {
computedDim[0] = elemDim[0];
computedDim[1] = "auto";
} else {
// elem's ratio is greater than or equal to img ratio
if (elemDim[0] / elemDim[1] >= ratio) {
computedDim[0] = "auto";
computedDim[1] = elemDim[1];
} else {
computedDim[1] = "auto";
computedDim[0] = elemDim[0];
}
}
} else {
// If not 'cover' or 'contain', loop through the values
for (var i = cssSize.length; i--; ) {
// Check if values are in pixels or in percentage
if (cssSize[i].indexOf("px") > -1) {
// If in pixels, just remove the 'px' to get the value
computedDim[i] = cssSize[i].replace("px", "");
} else if (cssSize[i].indexOf("%") > -1) {
// If percentage, get percentage of elem's dimension
// and assign it to the computed dimension
computedDim[i] = elemDim[i] * (cssSize[i].replace("%", "") / 100);
}
}
}
// If both values are set to auto, return image's
// original width and height
if (computedDim[0] === "auto" && computedDim[1] === "auto") {
computedDim[0] = image.width;
computedDim[1] = image.height;
} else {
// Depending on whether width or height is auto,
// calculate the value in pixels of auto.
// ratio in here is just getting proportions.
ratio =
computedDim[0] === "auto"
? image.height / computedDim[1]
: image.width / computedDim[0];
computedDim[0] =
computedDim[0] === "auto" ? image.width / ratio : computedDim[0];
computedDim[1] =
computedDim[1] === "auto" ? image.height / ratio : computedDim[1];
}
// Finally, return an object with the width and height of the
// background image.
return {
width: computedDim[0],
height: computedDim[1]
};
}
function abc() {
console.log(getBackgroundSize(document.querySelector(".img")));
}
</script>
</body>
</html>
any suggestion ?
?
Like the comments suggest you need to wait for the image to load. Here I added an event listener for the image object.
And maybe the image is already in the cache, but you need to wait for the image to load into this particular object.
var onImageLoad = e => {
let image = e.target;
let ratio =
image.width > image.height ?
image.width / image.height :
image.height / image.width;
console.log(ratio);
};
function getBackgroundSize(elem) {
var computedStyle = getComputedStyle(elem),
image = new Image(),
src = computedStyle.backgroundImage.replace(
/url\((['"])?(.*?)\1\)/gi,
"$2"
),
cssSize = computedStyle.backgroundSize,
elemW = parseInt(computedStyle.width.replace("px", ""), 10),
elemH = parseInt(computedStyle.height.replace("px", ""), 10),
elemDim = [elemW, elemH],
computedDim = [];
image.addEventListener('load', onImageLoad);
image.src = src;
}
function abc() {
getBackgroundSize(document.querySelector(".img"));
}
.img {
background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwIDEwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgZmlsbD0iZ3JheSIgLz4KPC9zdmc+") no-repeat left;
width: 200px;
height: 200px;
}
<div class="img"></div>
<button onclick="abc()">click</button>
This code when run should resize the height and width that a image to fit the container.
This is the output from the code(from alerts):
2488: Images natural height
3264: Images natural width
450: The containers height
1063: The containers width
612: New height
844: New width
4: The number of times it was divided to get to output
**It should divide it 6 times to provide the outcome of:
New width: 544
New height: 414
**
I am almost certain that the problem is in the Java Script:
function resize(iid, eid) {
//Get the ID of the elements (ele being the container that the image is in and img being the image its self)
var img = document.getElementById('img');
var ele = document.getElementById('contaner');
//makes the var needed
var currentwidth = ele.clientWidth;
var currentheight = ele.clientHeight;
var naturalheight = img.naturalHeight;
var naturalwidth = img.naturalWidth;
var newheight = naturalheight;
var newwidth = naturalwidth;
var x = 0;
//runs a loop that should size the image
while (newheight > currentheight && newwidth > currentwidth){
x = x + 1;
newheight = naturalheight / x;
newwidth = naturalwidth / x;
}
newheight = Math.ceil(newheight);
newwidth = Math.ceil(newwidth);
//alerts out the answers
alert(naturalheight);
alert(naturalwidth);
alert(currentheight);
alert(currentwidth);
alert(newheight);
alert(newwidth);
alert(x);
}
#contaner {
height: 450px;
width: 90%;
margin: 5% auto;
position: relative;
}
#img {
height: 450px;
width: 90%;
}
<div id="contaner">
<img src = "..\..\Resorces\Images\SlideShow\img1.jpg" style="width:652px;height:489px;" id="img"/>
<div id="left_holder"><img onClick="slide(-1)" src="..\..\Resorces\Images\arrow_left.png" class="left"/></div>
<div id="right_holder"><img onClick="slide(+1)" src="..\..\Resorces\Images\arrow_right.png" class="right"/></div>
</div>
The problem is this line:
while (newheight > currentheight && newwidth > currentwidth)
It's stopping as soon as either width or height fits within the container, where as it seems like you want both to fit within the bounds of the container. Change to || and you'll get six iterations:
while (newheight > currentheight || newwidth > currentwidth)
I have 3 random images and one fixed block (200x300px).
Please, help me to write an algoritm, I need to change image size proportionally to get into fixed block.
Images width must be equal of block width
http://jsfiddle.net/U8AAu/2/
var images = [
getRandSizes(),
getRandSizes(),
getRandSizes()
];
var sizes = getProportionalSizes(200, 300, images);
$.each(sizes, function(i, size){
var $img = $("<div>", {
class: 'img',
width: size[0],
height: size[1]
}).appendTo('.fixed-block')
});
// todo:
function getProportionalSizes(maxWidth, maxHeight, sizes){
return sizes;
}
function getRandSizes(){
return [getRand(100,200), getRand(100,200)]
}
function getRand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Always changing the image width to fill the box width will cause aspect ratio issues, and will distort your pictures. I would recommend doing something like this.
var image1 = new Object( );
var sizeArray = getRandSizes( );
image1.width = sizeArray[0];
image1.height = sizeArray[1]; //Repeat for images 2 and 3
var images =
[
image1,
image2,
image3
];
images = getProportionalSizes( 200, 300, images );
images.forEach( function( image )
{
var $img = $("<div>",
{
class: 'img',
width: image.width,
height: image.height
}).appendTo('.fixed-block')
});
function getProportionalSizes(maxWidth, maxHeight, images)
{
var totalHeight;
images.forEach( function( image )
{
totalHeight += image.height;
});
images.forEach( function( image )
{
var ratio = image.height / totalHeight;
image.height *= ratio;
image.width *= ratio; //This will ensure that images maintain aspect ratio, but that the total height of the 3 images matches the container height.
});
return images;
}
function getRandSizes()
{
return [getRand(100,200), getRand(100,200)]
}
function getRand(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
EDIT ------------------------
If having full block width is a requirement, and the distortion of the images is irrelevant, then do this instead.
function getProportionalSizes(maxWidth, maxHeight, images)
{
var totalHeight;
images.forEach( function( image )
{
totalHeight += image.height;
});
images.forEach( function( image )
{
var ratio = image.height / totalHeight;
image.height *= ratio;
image.width = maxWidth //This will keep the individual image height proportional to each other, but stretch the picture in the x-direction to fill the block.
});
return images;
}
To change the width of the image to a fixed block width of 200, change your size definition to:
width: 200,
height: size[1]*200/size[0]
This will preserve the aspect ratio while resizing the image to the proper width.
Note that it is possible that the resulting image will be taller than the height specified. If the original image is 100x200, the resulting resized image will be 200x400. This is unavoidable, given the problem constraints.
I've been using jslint to try see what it says about my code, and i get lots of flags, but i am working through trying improve it. However i am stuck on the error
maxHeight was used before it was defined
My jQuery:
$.fn.thumbSizr = function () { // begin function
"use strict";
return this.each(function () {
var $this = $(this);
maxWidth = $(this).parent().width(); // Max width for the image
minHeight = $(this).parent().height(); // Max height for the image
ratio = 0; // Used for aspect ratio
width = $(this).width(); // Current image width
height = $(this).height(); // Current image height
if(width > maxWidth){
ratio = maxWidth / width; // get ratio for scaling image
$(this).css("width", maxWidth); // Set new width
$(this).css("height", height * ratio); // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
// Check if current height is larger than max
if(height < minHeight){
ratio = minHeight / height; // get ratio for scaling image
$(this).css("height", minHeight); // Set new height
$(this).css("width", width * ratio); // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
}
var $img = $(this),
css = {
position: 'absolute',
marginLeft: '-' + (parseInt( $img.css('width') ) / 2) + 'px',
left: '50%',
top: '50%',
marginTop: '-' + (parseInt( $img.css('height') ) / 2) + 'px'
};
$img.css( css );
});
};
I'm no jQuery pro so this might be ropey but i really wanted to make it as good as possible. Can anyone explain and suggest why i am getting this message and how to avoid it in the future?
Thanks
You are using semicolon instead of comma when declaring multiple variable with single "var"
This part is wrong:
var $this = $(this);
maxWidth = $(this).parent().width(); // Max width for the image
minHeight = $(this).parent().height(); // Max height for the image
ratio = 0; // Used for aspect ratio
width = $(this).width(); // Current image width
height = $(this).height(); // Current image height
fixed:
var $this = $(this),
maxWidth = $(this).parent().width(), // Max width for the image
minHeight = $(this).parent().height(), // Max height for the image
ratio = 0, // Used for aspect ratio
width = $(this).width(), // Current image width
height = $(this).height(); // Current image height
The below given code resizes an image to 160x160 and works fine for Firefox & Chrome but not for Internet Explorer. Why?
$(document).ready(function() {
$('#imagePreview img').each(function() {
$('#imagePreview img').each(function() {
var maxWidth = 160; // Max width for the image
var maxHeight = 160; // Max height for the image
var ratio = 0; // Used for aspect ratio
var width = $(this).width(); // Current image width
var height = $(this).height(); // Current image height
// Check if the current width is larger than the max
if(width > maxWidth){
ratio = maxWidth / width;
$(this).css("width", maxWidth); // Set new width
$(this).css("height", height * ratio); // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
// Check if current height is larger than max
if(height > maxHeight){
ratio = maxHeight / height;
$(this).css("height", maxHeight); // Set new height
$(this).css("width", width * ratio); // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
}
});
});
});
You're not waiting for the images to be loaded, so they're not guaranteed to have a size (depending on whether they're cached).
You should replace
$(document).ready(function() {
with
$(document).load(function() {
This being said, it looks like you could replace the whole with this style :
#imagePreview img {
max-width: 160px;
max-height: 160px;
}
(see demonstration)