Preview and enlarge images similar to the way Firefox allows you to do

When you view an image in Firefox, it is displayed with the following characteristics:

  • Centered within the browser window.
  • Has a height/width that fits within the browser dimensions.
  • Does not resize larger than its maximum size.
  • Remains the same size if smaller than the browser window.
  • Maintains its aspect ratio.
  • Includes a zoom tool to resize the image to its maximum size and exceed browser dimensions.

I'm looking for a way to achieve this using CSS/JS. I've experimented with different CSS methods, but I believe it may require JS, and I haven't come across any examples.

The most successful results I've had involve using "contain" in the CSS:

height: 100%;
width: 100%;
background-url: {image location}
background-position: center center;
background-repeat: no-repeat;
background-size: contain;

However, this approach works best with larger images, as it tends to stretch smaller images to match the height or width of the browser window. Ideally, I want smaller images to stay centered and maintain their maximum height/width.

Answer №1

Check out the jsBin demo

(Please note: This demo is compatible with modern browsers only, such as IE9+)

All you need to achieve the desired effect is:

<div id="parent">
  <img src="image.jpg">
</div>

CSS:

html,
body{
  margin:0;
  height:100%;
  background:url(http://i.imgur.com/aSqDLP0.png);
}

body{ /* Alternatively, use a wrapper element */
  display: table;
  width: 100%;
  text-align: center;
}
#parent{
  display: table-cell;
  vertical-align: middle;
}
#parent img{
  vertical-align: middle;
  max-height: 100vh;
  max-width: 100vw;
  box-shadow: 0 4px 15px #111;
}

The above code is sufficient for centering and resizing.
If you want to mimic Firefox's behavior even more precisely:

  • Zoom-in cursor on hover - when the image is resized by the browser (small)
  • Automatically scroll the window to the clicked coordinates - if the image is small
  • Zoom-out cursor - when the image is zoomed (clicked) - show the -

A bit of jQuery can be helpful for this purpose:

var $parent = $("#parent"),
    isParentSmaller = false,
    zoom = false,
    parW, parH,
    imgW, imgH;

$parent.find("img").on("mouseenter click", function( e ){

  imgW = this.naturalWidth;
  imgH = this.naturalHeight;
  parW = $parent.width();
  parH = $parent.height();
  isParentSmaller = parW-1 < imgW || parH-1 < imgH;

  $(this).css({
    cursor: isParentSmaller ? (zoom?"zoom-out":"zoom-in") : "auto"
  });

  if(e.type=="click" && isParentSmaller){
    zoom = !zoom;       // Toggle zoom boolean  
    $(this).css({       // Apply cursor styles
      maxWidth  : zoom ? "none" : "100vw",
      maxHeight : zoom ? "none" : "100vh",
      cursor    : zoom ? "zoom-out":"zoom-in"
    });
    // Scroll window where we want to zoom:
    window.scrollTo(((imgW/parW)-1)*e.clientX, ((imgH/parH)-1)*e.clientY);
  }

});

In conclusion, the aforementioned approach performs even better than Firefox, especially in cases where Firefox loses the Magnifying glass cursor upon window resize :)

Answer №2

Note: I had to revise my answer because the initial solution was incompatible with Firefox (talk about irony). It also caused unexpected behaviors on different browsers due to flexbox usage for centering the image vertically and horizontally.

Let's break it down step by step.

To maintain the aspect ratio of an image while setting maximum dimensions, you can use the following CSS:

.img {
  display: block;
  max-height: 100%;
  max-width: 100%;
  height: auto;
  width: auto;
}

Centering an element both vertically and horizontally typically involves three lines of code using flexbox. However, this approach led to issues when scaling images on certain browsers. Instead, we opt for text-align: center for horizontal alignment and employ a technique involving a "Ghost Element" for vertical centering. For more details, refer to this CSS Tricks article. In essence, the centering method looks like this:

.parent {
  text-align: center;
  white-space: nowrap;
}

.parent:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  margin-right: -0.25em;
}

.centered-child {
  display: inline-block;
  vertical-align: middle;
}

Finally, combining scaling and centering, assuming that the HTML comprises only a single <img class="img" ...> in the body:

html {
  width: 100%;
  height: 100%;
}

body {
  margin: 0;
  width: 100%;
  height: 100%;
  background-color: #333;
  text-align: center;
}

body:before {
  content: '';
  width: 0;
  height: 100%;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
  margin-left: -0.25em;
}

.img {
  display: inline-block;
  vertical-align: middle;
  max-height: 100%;
  max-width: 100%;
  width: auto;
  height: auto;
}

Adding Zoom Functionality

To enable image zooming, JavaScript (specifically jQuery) is required.

Preferably, avoid directly altering CSS attributes via JavaScript by defining two additional classes:

.img.is-zoomable {
  cursor: zoom-in;
}

.img.is-zoomed {
  cursor: zoom-out;
  max-height: none;
  max-width: none;
}

Upon click, the script toggles the is-zoomed class, in addition to checking for zoom feasibility upon mouseover to apply the is-zoomable class if appropriate.

$('.img').on('click', function() {
    $(this).toggleClass('is-zoomed');
});
$('.img').on('mouseenter', function() {
    var origWidth = this.naturalWidth;
    var origHeight = this.naturalHeight;
    var currWidth = $(this).width();
    var currHeight = $(this).height();
    if (origWidth !== currWidth || origHeight !== currHeight) {
        $(this).addClass('is-zoomable');
    }
});

And there you have it! Check out my CodePen for a practical demonstration.

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

What is causing elements like divs, paragraphs, or text not to display within an ion-item after an ion-input is added?

I am currently working on validating a simple form and everything seems to be functioning properly. However, I have encountered an issue with displaying text messages within an ionic 3 list item. The ion-item consists of an ion-input element. When I place ...

I am looking to dynamically assign an href attribute to a link using a variable within my script. How can I achieve

I am working on displaying an image inside a jQuery slider with the following HTML markup:- <li> <figure> <a href="~/img/big.jpg" class="thumb"> <img src="~/img/small.jpg" alt=""/> <span> ...

Finding out which element is currently in focus in Internet Explorer 11 requires a few simple steps

Is there a way to determine which element is going to receive focus when the focusout event is triggered in Chrome and IE 6-10, or when the blur event is triggered in Firefox? $(input).focusout(function (event) { "use strict"; cons ...

Expanding on the nested document in mongoose

I have been working on setting up a nested mongoose document configuration in the following manner: models/data.js var mongoose = require('mongoose'); var addresses = new mongoose.Schema({ "street": String, "city": String, "state": Stri ...

Is it possible to execute a Google Script on the last row of a spreadsheet exclusively

I have an email script that is functioning correctly, but I only want it to run through the last row with data in it. If there is no data in a row, then I do not need the script to run for that specific row. How can I modify the example script below to ref ...

"Error: The $ variable is not defined" - encountered while working with a date-time picker in Jade/Pug

I am attempting to implement a date/time picker in jade/pug that resembles the example provided on this page: . I am working with a Node/express js server. However, when I click on the glyphicon-calendar icon, the calendar fails to display. I have already ...

Obtaining a 16-bit integer from the response of an LWIP server

On the server side, I have implemented a loop that takes a 16-bit integer ranging from 0 to 639 and splits it into two 8-bit characters to populate a buffer of 1280 Bytes. The buffer is then sent via TCP-IP to the client. .c unsigned int data2[1000]; ch ...

Learn how to implement the JQuery ReplaceWith function with the resources provided by materials.io icon

I would like the favorite_border icon to switch to the favorite icon when clicked. As we are using material.io and both icons have the class material-icons, I am unsure about how to implement this using jQuery. What steps should I take to achieve this? (w ...

Using the <video> element is creating a gap between itself and the element following it

I am facing an issue with a video background on my webpage. The video is perfectly positioned, but it is creating a large gap between itself and the next element. Despite checking for margins or paddings in the inspection tool, I cannot seem to find the so ...

What is the best way to make Bootstrap 3 move to a new line when there is not enough space?

I am facing an issue with a lengthy text inside a div with a class of "col-md-3". The text is so long that it appears to be overlapping with the content of another div. Adding spaces is not an option as it is an email address, and it displays fine on some ...

When using React-hook-form, you need to tap twice to delete the last character in the input field, and double tap to enter the first

I have been using React Hook Form to validate my form with multiple inputs. Everything works perfectly, except I noticed one issue where I have to press the backspace key twice to delete the last character and also twice to enter the first character. For e ...

Using a CSS gradient with a variable in React.js - A guide to implementation

Looking to incorporate the following CSS property into the Navlink component. In the CSS file, the property is usually written like this using gradient. .ele { background-image: linear-gradient(45deg, #808080 25%, transparent 25%), li ...

What is the importance of using ChangeDetectorRef.detectChanges() in Angular when integrating with Stripe?

Currently learning about integrating stripe elements with Angular and I'm intrigued by the use of the onChange method that calls detectChanges() at the end. The onChange function acts as an event listener for the stripe card, checking for errors upon ...

Initialize a jQuery UI resizable element by selecting a specific element that has been dynamically added to the DOM

Is there a way to start a jquery ui resizable instance using a selector added to the DOM by jquery itself? Here is a snippet of the script I am currently working with: HTML: <div class='lyr'></div> jQuery: // Add class $('lyr ...

What is the best method for transforming JSON into JSONP?

I have written the following code to send JSON data to the front end. However, we are facing cross-domain security issues and need to convert it to JSONP. Can anyone suggest what modifications I should make for this conversion? Server-side Code JsonFac ...

Creating a design that can adapt to different screen sizes by utilizing CSS

On standard computer screens, my div has a height of 100%. However, for mobile screens, I would prefer not to have the height set at 100%. Is there a way to remove the height: 100% that was originally set for regular screens using CSS media queries? ...

Determine the level of similarity between two images in terms of percentage

Is there a Python library or script available that can calculate the percentage of similarity between two images? If so, please specify which function performs this task. I am looking to develop a script similar to Google Image Search where I can input an ...

Fixing the issue with CSS scrollbar swapping

I'm currently working on a template and facing a challenge that I can't quite comprehend. More specifically, I understand why it's happening, but I'm struggling to implement a cross-browser solution. The issue revolves around a fixed b ...

The design of the website is all over the place

I am encountering challenges in creating distinct containers for the header, body, and other sections of my website. The layout is not turning out as planned, and I can't pinpoint where my code is going wrong. Any advice or suggestions on how to resol ...

Determine the existence of a document/record in MongoDB

I am having trouble using .find({}) with MongoDB as it is not returning the expected response. I'm not sure how to determine if the document exists or not. What I want to achieve is: If a document exists, then do something - like sending a response b ...