0

Check the following component (live example shown in this snippet https://codesandbox.io/s/reverent-mccarthy-r3v93?file=/src/App.tsx):

import * as React from "react";

export default function App() {
  const [indexOfDiagnosis, setIndex] = React.useState(0);

  return (
    <div>
      <button onClick={() => setIndex(indexOfDiagnosis + 1)}>click</button>
      <div>
        {[1, 2, 3, 4, 5, 6, 7].map((_, index) => (
          <img
            key={index}
            alt={"asd"}
            src={
              index % 2
                ? "https://images.unsplash.com/photo-1563630381190-77c336ea545a?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1872&q=80"
                : "https://images.unsplash.com/photo-1494548162494-384bba4ab999?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80"
            }
            width="80%"
            height="80%"
            style={{ height: index !== indexOfDiagnosis ? "0px" : undefined }}
          />
        ))}
      </div>
    </div>
  );
}

It shows a list of images, where all but one have height: 0 (these are implicitly hidden).

But, when you click the button to iterate through them, further images are shown with a top offset (as if the previous images still take some space). Why is this happening?

NOTE 1: In devtools the height, padding, border and margin of each "invisible" image is shown as 0.

NOTE 2: Also by setting the parent div to display: flex; flex-direction: column this offset bug does not happen.

croraf
  • 4,332
  • 2
  • 31
  • 50
  • 1
    At 2.5k you must know that you're required to put code _here_. :) Please see https://stackoverflow.com/help/how-to-ask. – isherwood Dec 07 '20 at 19:38
  • 2
    because the `img`'s are display inline by default. They are actually "replacement" elements and behave a little differently than other elements. set the `img`'s to `display:block;` and your problem goes away – zgood Dec 07 '20 at 19:46
  • 1
    If this is an answer, post it as an answer – croraf Dec 07 '20 at 19:48

2 Answers2

2

How to Fix
Most browser user agents will render img as a display inline-block. White-space will be added in between inline and inline-block elements if space exists between the HTML coding brackets.

To solve your problems the quickest way is to set each img element to display block.

Alternatively, here is how to remove space between inline elements: How do I remove the space between inline/inline-block elements?

img {
  width: 100px;
}
.block {
  display: block;
}
<!-- Defualt -->
<p> Default </p>
<img src="https://images.unsplash.com/photo-1563630381190-77c336ea545a?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1872&q=80"/>
<img src="https://images.unsplash.com/photo-1494548162494-384bba4ab999?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80"/>

<!-- Display Block -->
<p> Display Block </p>
<img class="block" src="https://images.unsplash.com/photo-1563630381190-77c336ea545a?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1872&q=80"/>
<img class="block" src="https://images.unsplash.com/photo-1494548162494-384bba4ab999?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80"/>

<!-- No Whitespace -->
<p> No Whitespace </p>
<img src="https://images.unsplash.com/photo-1563630381190-77c336ea545a?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1872&q=80"/><img src="https://images.unsplash.com/photo-1494548162494-384bba4ab999?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80"/>

Additional Suggestions
Instead of using the height: 0; property to hide an element, might I suggest using display: none; instead? This will truly hide it from the DOM. To unhide it from the DOM, set it to display: block;. About the Display Property

Of course, if you plan on working with animations, I would use the visibility property instead. About the Visibility Property

Another solution is to simply wipe the element off by setting both the width and height to 0 when not being used. Remove the attribute width and height and apply it to the style.

style={
  { 
    width: index !== indexOfDiagnosis ? "0px" : "500px",
    height: index !== indexOfDiagnosis ? "0px" : "200px" 
  }
}
Matthew Dangle
  • 426
  • 3
  • 13
  • 1
    You say that my {...} produces an HTML of `inline-blockinline-block...`, and that because of these the line preserves the height? – croraf Dec 07 '20 at 20:22
  • Yes, in static HTML this fixes it. The same would apply to any JS library depending how the HTML is inserted by the library and user. – Matthew Dangle Dec 07 '20 at 20:25
  • I did try the static HTML example yesterday and I didnt notice the same problem: https://codesandbox.io/s/image-taking-space-in-block-layout-dqv99?file=/index.html – croraf Dec 07 '20 at 20:27
  • If I compare the HTML in the devtools of this plain HTML example and the React's one I don't see any difference. The whitespace between elements cannot be spotted in the devtools. – croraf Dec 07 '20 at 20:32
  • That is odd... I will take another look. Any reason why you cannot use `display: block;` though? – Matthew Dangle Dec 07 '20 at 20:33
  • I think display:block; is fine. The solution I'm currently using is "display: flex" on the parent element, as I described in the question. Also I tried to output the `el.textContent` of the parent element. And in plain HTML example it outputs 4-5 newlines, versus in React example where it outputs an empty string. Which is the opposite of what I expected. – croraf Dec 07 '20 at 20:38
  • Last resort alternative is setting the `font-size` to `0`, which will remove all white space characters, including spaces. I would say last resort as it removes the cascading effect inherited by parent elements. – Matthew Dangle Dec 07 '20 at 20:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225638/discussion-between-croraf-and-matthew-r-dangle). – croraf Dec 07 '20 at 20:44
2

Setting the height of an image to 0 isn't enough to 'hide' it from the DOM.

The most common way to hide an image is to use display: none as described here.

style={{ display: index === indexOfDiagnosis ? "block" : "none" }}

Updated sandbox


EDIT: Since OP is looking for a solution without display:none, here an example using the provided height: 0px together with an font-size: 0px on the parent element to prevent any space between the blocks.

<div style={{ fontSize: "0px" }}>

How do I remove the space between inline/inline-block elements?

Updated sandbox

0stone0
  • 34,288
  • 4
  • 39
  • 64
  • Thanks. display: none doesn't fit me because of some other things. – croraf Dec 07 '20 at 20:02
  • @croraf Could you clarify why you can't use `display:none`? Maybe you could add it to the question for further readers. – 0stone0 Dec 07 '20 at 20:03
  • It is not that relevant. I have a bigger tree, and when doing display: none to the ancestor of the images they don't get loaded over the network until shown. – croraf Dec 07 '20 at 20:05
  • 2
    @croraf, thats a shame ;) Please check my edit, an other option using `font-size: 0px` to prevent whitespaces, please check the linked post! – 0stone0 Dec 07 '20 at 20:10
  • 2
    Thanks. font-size: 0 indeed solves the problem. Well done. I tried line-height: 0 but that didnt do it :| . Although it did reduce the height – croraf Dec 07 '20 at 20:16
  • @croraf Glad it worked, I guess you should pick the most suiting fix mentioned in the linked post for your problem. Please consider upvoting/accepting any answers that helped. – 0stone0 Dec 07 '20 at 20:23