0

I created a modal image project similar to this.

When I click on the image I want to display and scale up the modal image, and it should scale down and disappear when I click the close button. However, when I use the display property for hiding and displaying the modal image, the transform: scale property doesn't work, and the modal image will be displayed and hidden suddenly.

I commented out document.getElementById("modalimg").style.display = "none"; which did improve things, but not enough.

I also tried to run this with the visibility property instead of the display property and it worked well - but I want to know what the problem is.

I also searched for similar questions, and found this and this, but they didn't help.

Note: the code snippet will need to be run in fullscreen to display properly.

document.getElementById("container").onclick = modal;

function modal(buttFlag) {
    document.getElementById("modalimg").style.display = "inline";
    document.getElementById("modalbackground").style.display = "block";
    document.getElementById("butt").style.display = "block";
    document.getElementById("modalimg").style.transition = "all 1s";
    document.getElementById("modalimg").style.transform = "scale(7,4)";

    if (buttFlag == true) {
        document.getElementById("modalimg").style.transform = "scale(1,1)";
        document.getElementById("modalimg").style.display = "none";//I tried comment this line and it worked better but not good enough.
        
        document.getElementById("butt").style.display = "none";
        document.getElementById("modalbackground").style.display = "none";
        buttFlag = false;
    }
}
#container{
    width: 20%; height: 50%;
    margin: 70%; margin-top: 12%;
    position: absolute;
}
#container img{
    width: 100%; height: 100%;
    object-fit: cover;
}
#modalbackground{
    position: absolute;
    width: 100%;height: 100%;
    background-color: black;
    opacity: 0.7;
    display: none;
    z-index: +1;
}
#modalimg{
    width: 100px; height: 100px;
    position: absolute;
    top:45%; left: 45%;
    z-index: +1;
    display: none;
}
button{
    font-size: 2em;
    background-color: transparent;
    border: none;
    position: absolute;
    left: 85%; top: 5%; 
    color: white;
    z-index: +1;
    display: none;
}
<div id="modalbackground"></div>

<div id="container">
    <img src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
</div>

<button onclick="modal(true)" id="butt">&#9746;</button>
  
<img id="modalimg" src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg"                 alt="jungle">
jla
  • 4,191
  • 3
  • 27
  • 44
Sepehr
  • 39
  • 5

1 Answers1

1

Why the issue happens

When an element has display: none, it's not just hidden from view, the browser doesn't include it at all in the DOM. Furthermore, when inline styling changes are applied by JavaScript, the browser doesn't render them one at a time, but waits a short while and then renders them all at once.

So when you have a element with display: none, if you do

element.style.display = 'block';
element.style.transform = 'scale(2);

the browser won't stop at the first line to draw the element, and then go to the next line and apply the inline style of scale(2). It will prepare everything there in its internal representation, and when it decides that it's got time it will dump the entire element in. Therefore, you don't get an element that starts at scale(1) and gets animated to scale(2), the element is drawn on the page already rendered at scale(2).

Solving the issue

To get around this, you have to apply display: block, and then wait a little while until we're sure the browser has drawn the element. Once we're sure the element has been drawn to the screen (at scale(1)) we then apply the style transform: scale(2). This will get animated.

There are built in ways to know exactly how long to wait, but an easy rule of thumb is to remember that 60Hz screens refresh roughly once every 16ms, so if you wait 20ms you should be good. The standard way to wait a specified length of time in JavaScript is with setTimeout. Our previous code would look like:

element.style.display = 'block';
setTimeout( function() {
  element.style.transform = 'scale(2)';
}, 20 );

Reversing it

This is all well and good for animating the appearing of the model, but what about when you close the modal?

Well it's just the same, but in reverse. This time the length of time you have to wait is how long the animation runs, so that you don't get display: none running when you want the animation to be happening. If our transform transition was to last for 1 second, our code from above would look like the following when making it disappear:

element.style.transform = 'scale(2)';
setTimeout( function() {
  element.style.display = 'block'; // will wait 1000ms to run
}, 1000 );

Applying to your code

Applying this to your code would look like the following: (note; there were a couple of bugs when calling the modal that have been patched up, but they aren't relevant to the general idea).

document.getElementById("container").onclick = modal;

function modal(buttFlag) {
  const modalimg = document.getElementById("modalimg");
  const modalbackground = document.getElementById("modalbackground");
  const butt = document.getElementById("butt");

  if ( buttFlag !== true ) { // not the close button
    modalbackground.style.display = "block";
    modalimg.style.display = "block";
    butt.style.display = "block";
    
    setTimeout( function() {
      modalimg.style.transform = "scale(7,4)";
    }, 20 );
  }

  if ( buttFlag === true ) { // is te close button
    modalimg.style.transform = "scale(1)";
    
    setTimeout( function() {
      modalimg.style.display = "none";
      modalbackground.style.display = "none";
      butt.style.display = "none";
    }, 1000 );
  }
}
#container{
    width: 20%; height: 50%;
    margin: 70%; margin-top: 12%;
    position: absolute;
}
#container img{
    width: 100%; height: 100%;
    object-fit: cover;
    position: relative;
    z-index: 5;
}
#modalbackground{
    position: absolute;
    width: 100%;height: 100%;
    background-color: black;
    opacity: 0.7;
    display: none;
    z-index: 10;
}
#modalimg{
    width: 100px; height: 100px;
    position: absolute;
    top:45%; left: 45%;
    z-index: 11;
    display: none;
    transform: scale(1);
    transition: transform 1s;
}
button{
    font-size: 2em;
    background-color: transparent;
    border: none;
    position: absolute;
    left: 85%; top: 5%; 
    color: white;
    z-index: 12;
    display: none;
}
<div id="modalbackground"></div>

<div id="container">
    <img src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
</div>

<button onclick="modal(true)" id="butt">&#9746;</button>
  
<img id="modalimg" src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg"                 alt="jungle">
jla
  • 4,191
  • 3
  • 27
  • 44
  • as you mentioned:" when inline styling changes are applied by JavaScript, the browser doesn't render them one at a time, but waits a short while and then renders them all at once", I want to know that it is the behavior of the browser or javascript language? and it is just for inline styles or all kinds of styles and codes? could you recommend a reference for how javascript renders codes? @jla – Sepehr Jun 20 '23 at 12:18
  • @Sepehr how long before the browser re-renders changes to the DOM isn't specified in the JavaScript language, it depends on the JavaScript engine (eg Blink for Chrome, JavScriptCore for Webkit, etc) and will vary in the exact process between browsers. Any changes that require repainting the window will have some sort of delay; this applies to much more than just inline styles. Some useful reading is https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work especially the sections Parsing and Render – jla Jun 20 '23 at 23:11