Pieter Wouters
Pieter Wouters

Reputation: 35

Detecting orientation of images with Javascript

I'm trying to find a way to detect the orientation of images (landscape or portrait).

The HTML simply contains a few img tags with source already filled in. The script should then detect the orientation and add the images to dynamically created divs acting as thumbnails.

The script I've been using is this (found it on here somewhere)

    for (i = 0; i < pics.length; i++) {
      pics[i].addEventListener("load", function() {
        if (this.naturalHeight > this.naturalWidth) {
          this.classList.add("portrait")
        } else {
          this.classList.add("landscape")
        }
     })
    }

Now this usually works fine when first loading the page. When refreshing however, it behaves erratically, adding the right class to some images and not adding any class at all to others.

I also tried this

for (i = 0; i < pics.length; i++) {
  var img = pics[i];
  var width = img.naturalWidth;
  var height = img.naturalHeight;
  if (height > width) {
    img.classList.add("portrait")
  } else {
    img.classList.add("landscape")
  }
}

Which also works unpredictably. All images get a class added to them, but some get the wrong one.

I'm guessing the problems might come from the images not completely loading before the script runs and thus the script not being able to measure them correctly, but I'm not sure. Anyhow, I don't really know how to fix it either.

To give an idea of what I'm going for, here's a link to the page: http://pieterwouters.tumblr.com/

Any ideas much appreciated.

Upvotes: 3

Views: 4786

Answers (3)

Pieter Wouters
Pieter Wouters

Reputation: 35

Thanks for the help!

ConnorsFan, based on your earlier comment I added a part to take into account already loaded images and came up with this

// forgot to mention this part in my original post
var pics = document.getElementsByTagName("img");

for (i = 0; i < pics.length; i++) {
    pics[i].addEventListener("load", function() {
        if (this.naturalHeight > this.naturalWidth) {
            this.classList.add("portrait")
        } else {
            this.classList.add("landscape")
        }
     })  
     if (pics[i].complete) {
         if (pics[i].naturalHeight > pics[i].naturalWidth) {
             pics[i].classList.add("portrait")
         } else {
             pics[i].classList.add("landscape")
         }
     }
 }

Which works, your solution seems a bit more elegant though so I'll probably use that one.

Upvotes: 0

Martin Parenteau
Martin Parenteau

Reputation: 73761

If the image is already loaded when you call pic.addEventListener("load", ...), the load event handler will not be triggered. You should call it if pic.complete is true, as suggested in this answer.

var pics = document.querySelectorAll("img");
var pic;

for (i = 0; i < pics.length; i++) {
  pic = pics[i];
  if (pic.complete) {
    // The image is already loaded, call handler
    checkImage(pic);
  } else {
    // The image is not loaded yet, set load event handler
    pic.addEventListener("load", function() {
      checkImage(this);
    })
  }
}

function checkImage(img) {
  if (img.naturalHeight > img.naturalWidth) {
    img.classList.add("portrait")
  } else {
    img.classList.add("landscape")
  }
}
img {
  border: 1px solid;
}

.landscape {
  border-color: red;
}

.portrait {
  border-color: blue;
}
<img src="//placehold.it/50x100">
<img src="//placehold.it/200x100">

Thanks to Ricky. I borrowed the image links and the style attributes from his answer.

Upvotes: 3

Ricky Ruiz
Ricky Ruiz

Reputation: 26791

For a safer but more expensive approach you could duplicate the images in memory and add the corresponding class.

const images = [...document.querySelectorAll('img')].map(el => {
  return new Promise((resolve, reject) => {
    let img = new Image();
    img.addEventListener('load', function() {
      const {
        naturalHeight,
        naturalWidth
      } = img;
      if (naturalHeight > naturalWidth) el.classList.add("portrait");
      else el.classList.add("landscape");
      img = null; //GC
      resolve();
    });
    img.src = el.src;
  });
});
img {
  border: 1px solid;
}

.landscape {
  border-color: red;
}

.portrait {
  border-color: blue;
}
<img src="//placehold.it/50">
<img src="//placehold.it/50x100">
<img src="//placehold.it/200x100">
<img src="//placehold.it/50x60">
<img src="//placehold.it/10x30">
<img src="//placehold.it/50x20">

Upvotes: 2

Related Questions