Is there a callback or event that can be used to ensure that getComputedStyle() returns the actual width and height values?

Currently, I find myself in a situation where I need to wait for an image to load before obtaining its computed height. This information is crucial as it allows me to adjust the yellow color selector accordingly.

Question: The process of setting the yellow color selector based on the computed height of the image currently relies on a random setTimeout() function. However, I am seeking a more structured approach.

let images = ['https://via.placeholder.com/150','https://via.placeholder.com/110/0000FF/808080%20?Text=Digital.com','https://via.placeholder.com/80/0000FF/808080%20?Text=Digital.com'];

let image = `<img src="${images[Math.floor(Math.random()*images.length)]}"/>`


document.getElementById('content').innerHTML = `<div class="box">${image}</div>`;

//actual code

let height = window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height');

let imageWidth = window.getComputedStyle(document.querySelector('.box img'), null).getPropertyValue('width');

console.log('height',height,'width',imageWidth);

wrapImage = `<div style="width:calc(${imageWidth} + 10px);height:calc(${height} + 10px);position:absolute;left:0;top:0;border:1px solid yellow;"></div>`;

document.querySelector('.box').insertAdjacentHTML('beforeend',wrapImage);
.box{
   width:100%;
   height:auto;
   border:1px solid red;
   position:relative;
}
<div id="content">

</div>

Although setTimeout works temporarily, I am exploring alternative methods, such as using a callback or event listeners upon element readiness.

let images = ['https://via.placeholder.com/150','https://via.placeholder.com/110/0000FF/808080%20?Text=Digital.com','https://via.placeholder.com/80/0000FF/808080%20?Text=Digital.com'];

let image = `<img src="${images[Math.floor(Math.random()*images.length)]}"/>`


document.getElementById('content').innerHTML = `<div class="box">${image}</div>`;

//actual code

setTimeout(() => {
   let height = window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height');

let imageWidth = window.getComputedStyle(document.querySelector('.box img'), null).getPropertyValue('width');

console.log('height',height,'width',imageWidth);

wrapImage = `<div class="select" style="width:calc(${imageWidth} + 10px);height:${height};position:absolute;left:0;top:0;border:1px solid yellow;"></div>`;

document.querySelector('.box').insertAdjacentHTML('beforeend',wrapImage);

document.querySelector('.select').height = document.querySelector('.select').height + 10;

console.log('after computed height and added 10px',window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height'));

},700);
.box{
   width:100%;
   height:auto;
   border:1px solid red;
   position:relative;
}
<div id="content">

</div>

Please assist me with finding a more effective solution. Thank you in advance!

Answer №1

The dimensions of the image may not be accurate if they are retrieved before the image has completely loaded. To ensure accuracy, it is important to wait for the images to finish loading before obtaining their height and width.

You can achieve this by listening for the window load event, which signals when all resources, including images, have finished loading:

let images = ['https://via.placeholder.com/150', 'https://via.placeholder.com/110/0000FF/808080%20?Text=Digital.com', 'https://via.placeholder.com/80/0000FF/808080%20?Text=Digital.com'];

let image = `<img src="${images[Math.floor(Math.random()*images.length)]}"/>`

document.getElementById('content').innerHTML = `<div class="box">${image}</div>`;

//actual code

window.addEventListener('load', function() {
  let height = window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height');

  let imageWidth = window.getComputedStyle(document.querySelector('.box img'), null).getPropertyValue('width');

  console.log('height', height, 'width', imageWidth);

  wrapImage = `<div class="select" style="width:calc(${imageWidth} + 10px);height:${height};position:absolute;left:0;top:0;border:1px solid yellow;"></div>`;

  document.querySelector('.box').insertAdjacentHTML('beforeend', wrapImage);

  document.querySelector('.select').height = document.querySelector('.select').height + 10;

  console.log('after computed height and added 10px', window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height'));

});
.box {
  width: 100%;
  height: auto;
  border: 1px solid red;
  position: relative;
}
<div id="content">

Answer №2

To highlight selected items, you can utilize the CSS outline property. This way, you won't need to handle selection dimensions manually.
Here is a demonstration code that includes 3 images for multiple selections:

function changeImages() {
  var images = document.getElementsByTagName('img');
  for (var i = 0; i < images.length; i++) {
    images[i].src = "https://via.placeholder.com/" + Math.floor(Math.random() * 50 + 50).toString() + "/0a0a0a/ffffff";
  }
}

function setClickEvents() {
  var images = document.getElementsByTagName('img');
  for (var i = 0; i < images.length; i++) {

    images[i].addEventListener("click", function(event) {
      event.target.classList.toggle("selected");
    });;

  }
}

function init() {
  document.getElementById('content').innerHTML = `<div id="box"></div>`;

  var box = document.getElementById('box');
  var img = document.createElement("img");
  img.classList.add("selected");
  box.appendChild(img);
  box.appendChild(document.createElement("img"));
  box.appendChild(document.createElement("img"));

  setClickEvents();
  changeImages();
}
#content {
  padding: 15px;
  margin: 10px;
}

#box {
  width: 100%;
  height: 120px;
  border: 1px dotted red;
  position: relative;
}

img {
  margin: 15px;
}


/* use this class for marking selected elements */
.selected {
  outline: thick double #f7d205;
  outline-offset: 7px;
}
<!DOCTYPE html>
<html lang="en">

<body onload="init()">
  <button onclick="changeImages()">Change Images</button>

  <div id="content"></div>
</body>

</html>

Simply click on an image to toggle its selection.

Answer №3

If you're looking for increased flexibility, consider utilizing the Resize Observer. This allows you to adjust the selection size when changing the src attribute of an image tag.

const imageObserver = new ResizeObserver(function(entries) {
  for (let entry of entries) {
    var img = entry.target;
    let height = img.height + 10;
    let width = img.width + 10;

    console.log('Added 10px. height:', height, ' width:', width);

    wrapImage = `<div class="select" style="width:${width}px;height:${height}px;position:absolute;left:0;top:0;border:1px solid #f7d205;"></div>`;
    document.querySelector('.box').insertAdjacentHTML('beforeend', wrapImage);
  }
});

function initialize() {
  document.getElementById('content').innerHTML = `<div id="box" class="box"></div>`;

  var box = document.getElementById('box');
  var img = document.createElement("img");
  img.src = "https://via.placeholder.com/" + Math.floor(Math.random() * 50 + 80).toString() + "/0a0a0a/ffffff";
  box.appendChild(img);
  imageObserver.observe(img);
}
<!DOCTYPE html>
<html lang="en">
<style>
  #box {
    width: 100%;
    border: 1px dotted red;
    position: relative;
  }
</style>

<body onload="initialize()">
  <div id="content"></div>
</body>

</html>


Note: You can use the same ResizeObserver to observe multiple images:

var images = document.getElementsByTagName('img');
for (var i = 0; i < images.length; i++) {
  imageObserver.observe(images[i]);
}

Edit: By request, demonstrating how to observe the resizing of an image from a parent div element. Here, the image is enclosed in the #box div. When the image is resized, it triggers a custom event that the parent handles.

function handleChildResizing(event) {
  console.log('parent: received it! handling now.. ')
  var img = event.data;
  let height = img.offsetHeight + 10;
  let width = img.offsetWidth + 10;

  console.log('Added 10px. height:', height, ' width:', width);

  wrapImage = `<div class="select" style="width:${width}px;height:${height}px;position:absolute;left:0;top:0;border:2px solid #f7d205;"></div>`;
  if (document.querySelector('.box > .select')) {
    document.querySelector('.box > .select').remove();
  }
  document.querySelector('.box').insertAdjacentHTML('beforeend', wrapImage);
  event.stopPropagation();
}

const imgObserver = new ResizeObserver(function(entries) {
  for (let entry of entries) {
    var img = entry.target;
    var event = new Event('childResized');
    event.data = img;
    console.log("img: i was resized. Broadcasting an event.");
    img.dispatchEvent(event);
  }
});

function initialize() {
  var box = document.getElementById('box');
  box.addEventListener('load', (event) => {
    console.log('The page has fully loaded');
  });
  var img = document.createElement("img");
  img.src = "https://via.placeholder.com/" + Math.floor(Math.random() * 50 + 80).toString() + "/0a0a0a/ffffff";
  box.appendChild(img);
  imgObserver.observe(img);
  box.addEventListener('childResized', handleChildResizing, true);
}
<!DOCTYPE html>
<html>
<head>
  <style>
    #box {
      width: 100%;
      padding: 10px;
      border: 1px solid red;
      position: relative;
    }
  </style>
</head>

<body onload="initialize()">
  <div id="content">
    <div id="box" class="box"></div>
  </div>
</body>

</html>

Answer №4

One option to consider is using the 'load' event listener as a callback for image loading. Take a look at this example:

const picture = document.getElementById('picture');
const handleImageLoad = () => {
  alert(picture.height);
};

picture.addEventListener('load', handleImageLoad);
<img src="https://image.shutterstock.com/z/stock-vector-sample-stamp-grunge-texture-vector-illustration-1389188336.jpg" id="picture" />

Answer №5

It appears that you are dynamically creating your imgNode using a ternary method, rather than having it pre-defined in your HTML. In order to address this issue, you can implement the following solution by utilizing an Image constructor.

const images = [
  "https://via.placeholder.com/150",
  "https://via.placeholder.com/110/0000FF/808080%20?Text=Digital.com",
  "https://via.placeholder.com/80/0000FF/808080%20?Text=Digital.com"
];

const img = new Image();

img.addEventListener("load", (ev) => {
  console.log(ev);

  document.getElementById(
    "content"
  ).innerHTML = `<div class="box">${ev.target}</div>`;

  const height = window
    .getComputedStyle(document.querySelector(".box"), null)
    .getPropertyValue("height");

  const imageWidth = window
    .getComputedStyle(document.querySelector(".box img"), null)
    .getPropertyValue("width");

  console.log("height", height, "width", imageWidth);

  const wrapImage = `<div style="width:calc(${imageWidth} + 10px);height:calc(${height} + 10px);position:absolute;left:0;top:0;border:1px solid yellow;"></div>`;

  document.querySelector(".box").insertAdjacentHTML("beforeend", wrapImage);
});

img.src = `${images[Math.floor(Math.random() * images.length)]}`;

Answer №6

If you're looking to calculate the final value of the computed style that you're observing, consider creating a custom function for it. The getComputedStyle method doesn't automatically update, so implementing a function is the way to go!

let images = ['https://via.placeholder.com/150','https://via.placeholder.com/110/0000FF/808080%20?Text=Digital.com','https://via.placeholder.com/80/0000FF/808080%20?Text=Digital.com'];'

let image = `<img src="${images[Math.floor(Math.random()*images.length)]}"/>`


document.getElementById('content').innerHTML = `<div class="box">${image}</div>`;

//actual code

setTimeout(() => {
   let height;
   let imageWidth;
   function calculateHeightAndWidth() {
        
height = window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height');

imageWidth = window.getComputedStyle(document.querySelector('.box img'), null).getPropertyValue('width');

   }
   calculateHeightAndWidth()


console.log('height',height,'width',imageWidth);

wrapImage = `<div class="select" style="width:calc(${imageWidth} + 10px);height:${height};position:absolute;left:0;top:0;border:1px solid yellow;"></div>`;

document.querySelector('.box').insertAdjacentHTML('beforeend',wrapImage);

document.querySelector('.select').height = document.querySelector('.select').height + 10;

calculateHeightAndWidth()
console.log('after computed height and added 10px',window.getComputedStyle(document.querySelector('.box'), null).getPropertyValue('height'));

},700);
.box{
   width:100%;
   height:auto;
   border:1px solid red;
   position:relative;
}
<div id="content">

</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Repeated instances in a loop are strictly prohibited

I am facing an issue with AngularJS where I am unable to display JSON data in my HTML. Below is the code I am using: $('.loading').show(); $scope.posts=[]; $http.get("https://api.basebear.com/json/tables/20a3fb1d-42c7-45cb-9440-2c6d5d10403d/r ...

Utilizing Props in Vue.js to Access Data for v-model

After browsing online, I attempted to pass props to data in the following manner: Child Component: props: { idInput: { type: String, required: false }, nameInput: { type: String, required: false }, }, data() { return { id: this.idInput, na ...

What is the best way to achieve a perfect rounded div using Bootstrap 4?

While browsing through the Bootstrap documentation and searching on stackoverflow, I came across a solution to create circular images using border-radius set at 50%. However, when I tried implementing it with my slightly oversized image, it ended up lookin ...

Angular JS sending a message to various views, including a pop-up modal

One of the services I have created is designed to manage message broadcasting to controllers that require it. The service implementation: .factory('mySharedService', function($rootScope) { var sharedService = {}; sharedService.message = &a ...

Access SCSS variable values in Angular HTML or TypeScript files

So, I've been looking into whether it's feasible to utilize the SCSS variable value within HTML or TS in Angular. For instance: Let's say I have a variable called $mdBreakpoint: 992px; stored inside the _variable.scss file. In my HTML cod ...

What sets Import apart from require in TypeScript?

I've been grappling with the nuances between import and require when it comes to using classes/modules from other files. The confusion arises when I try to use require('./config.json') and it works, but import config from './config.json ...

an li element is accompanied by a visible box

In my possession is a container: #box1 { height:100px; width:208px; } along with a series <li id="first"><strong>FIRST</strong> </li> <li id="second"><strong>SECOND</strong&g ...

The i18n feature in Nuxt 3 retrieves language locales from an external API

While working on my Nuxt 3 app, I encountered an issue when trying to integrate i18n. Despite conducting extensive research, I couldn't find any helpful information, hence I have a question. I am utilizing i18n with Prismic CMS. The locales array is s ...

Is there a way to toggle a single Reactstrap Collapse component?

Currently, I am working on a Next.JS application that displays a list of Github users. The goal is to have each user's information box toggle open and close when clicked, using Reactstrap's Collapse component. However, the issue I'm facing i ...

Is there a way to replicate the ctrl-F5 function using jQuery?

Is there a way to use jQuery to refresh the page and clear the cache at the same time? ...

What is the best way to reference an ImageButton using jquery?

I created an HTML page with a special div in the body that contains a collection of buttons and images. <div id="div_one"> <button id="button_one"> <asp:Image id="image_button_one" ImageUrl=".." runat="server"> </button> </div& ...

Sub-menu disappears upon resizing the screen

Currently, I am working on creating a unique responsive navigation system that transforms into a 100% width pulldown menu for mobile devices. To achieve this functionality, I have implemented some JavaScript code that hides any open sub-menu items when the ...

Using AngularJS to incorporate ng-include with ng-click functionality

I am trying to figure out a way to insert HTML that is specifically optimized for the controller into an alert div. Unfortunately, I have been unsuccessful so far... <script type="text/ng-include" id="login.html"> <form data-select="exepti ...

Show method created by function, substituting the former element on the display

showButtons(1) will show radio buttons for frame number 1, showButtons(400) will display radio buttons for frame number 400. The current code shows all radio buttons for every frame up to 400 HOWEVER, I am aiming for a single set of radio buttons to start ...

What is the best way to duplicate the table header?

I'm looking to have the table header repeat twice using ng-repeat, like this: a b | a b | Instead of aa | bb. Currently, my code only displays a b | <table class="table table-striped table-bordered"> <thead> ...

Error code TS7053 occurs when an element implicitly has an 'any' type because a string expression cannot be used to index an empty object

I have implemented a code snippet that sorts items into groups based on their first character. For example, if the array of item looks like this: {name: 'Foo'} {name: 'Bar'} {name: 'Baz'} The expected result should be: B: ...

Are there any aesthetically pleasing CSS themes available for GWT integration?

This inquiry has been raised in the past... GWT 2.0 Themes? GWT Themes and Component Libraries ...however, two years have elapsed. Previously, the response was mainly negative unless utilizing a widget library. I am on the lookout for an appealing CSS ...

Determine the total of the final column in recently added rows by utilizing JavaScript

I have a table where users can dynamically add rows as needed. I am looking to implement a feature that will display the total of the last column in a text box underneath the table using JavaScript. If real-time calculations are not feasible, then I am ope ...

Align the headers of columns to the right in the AgGrid widget

Is there a way to align the column headers to the right in AgGrid without having to implement a custom header component? It seems like a lot of work for something that should be simple. You can see an example here: https://stackblitz.com/edit/angular-ag-g ...

Using JavaScript to dynamically alter the background image of an HTML document from a selection of filenames

Just starting out with JavaScript and working on a simple project. My goal is to have the background image of an HTML document change to a random picture from a directory named 'Background' every time the page is opened. function main() { // ...