0

I am having problems with my JavaScript code. I am implementing a card game where I click a button and 13 cards are supposed to show up in intervals.

$("button").click(function() {
 let i = 0;
 setInterval(function() {
   if(i == 4) clearInterval();
   $(".block").eq(i).css({visibility:"visible"});
   $(".block").eq(i).html("TEXT" + i);
    i++;
  },100);
});
.block {
  display: inline-block;
  width: 100px;
  height: 140px;
  border: 2px solid;
  visibility: hidden;
}

button {
  position: absolute;
  top: 170px;
  left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
  <body>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <button id="button">Generate!</button>
  </body>
</html>

As seen above, I use the setInterval() function to display them with 100ms intervals, all the divs do what I tell them to do but they first appear quite below where I want them to be. How can I make it so that they appear in the correct places directly?

Thanks in advance!

Denis Tsoi
  • 9,428
  • 8
  • 37
  • 56
karirogg
  • 128
  • 8

5 Answers5

1

Add vertical-align: top; to your inline elements

$("button").click(function() {
 let i = 0;
 setInterval(function() {
   if(i == 4) clearInterval();
   $(".block").eq(i).css({visibility:"visible"});
   $(".block").eq(i).html("TEXT" + i);
    i++;
  },100);
});
.block {
  display: inline-block;
  width: 100px;
  height: 140px;
  border: 2px solid;
  visibility: hidden;
  vertical-align: top;
}

button {
  position: absolute;
  top: 170px;
  left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
  <body>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <button id="button">Generate!</button>
  </body>
</html>
epascarello
  • 204,599
  • 20
  • 195
  • 236
1

Setting the blocks to display: none and then adding display: inline-block is a way of getting around the problem, but doesn't fix the problem itself.

The main issue is the vertical-align property set on the block class. By default, this is set to baseline. Before your button is clicked, all your divs are lined up in a row, invisible, with their baseline set to the bottom of the div. However, when the button is clicked, not only do your blocks become visible, but more crucially, you add some text inside the div. This changes the baseline, making it the bottom of the text within the div instead. However, because of vertical-align: baseline, the baselines of all the divs in the row try to align. The baseline of the visible divs with text has to align with the baseline of the invisible divs with no text. But their baselines are now different, so the only way they can all sit in a straight line on their baselines would be if the divs with text are pushed down.

I've simplified your snippets to show you what I mean. I've made the divs visible, removed the button, and instead, have manually added some text into your divs in html. As you can see, for the divs with text, the bottom of the text aligns with the bottom of the div without text.

body {
  background: white;
 }
 
.block {
  display: inline-block;
  width: 100px;
  height: 140px;
  border: 2px solid;
}
<html>
  <body>
    <div class="block">TEXT</div>
    <div class="block">TEXT</div>
    <div class="block"></div>
    <div class="block">TEXT</div>
  </body>
</html>

The reason why changing the blocks to display: none in the beginning, and then displaying them one by one works is because in this case, there is never a point when textless divs and divs with text are present in the DOM at the same time, so there is never a mismatch of baselines. The divs enter the DOM with text in them, and so their baselines always match up. However, this doesn't entirely fix the issue. If the text in the divs were of different lengths, for instance, the bottom of the multiline text would match up with the bottom of the single-line text, resulting in misalignment once again.

Example:

body {
  background: white;
 } 

.block {
  display: inline-block;
  width: 100px;
  height: 140px;
  border: 2px solid;
}
<html>
  <body>
    <div class="block">text</div>
    <div class="block">text</div>
    <div class="block">very long text which takes up more than one line</div>
    <div class="block">text</div>
  </body>
</html>

So the proper fix for this would be to add vertical-align: top to the block class, to make sure that our alignment doesn't jump all over the place in response to the changing baseline.

royranger
  • 384
  • 1
  • 3
  • 10
0

You can put a wrapper div around the .block elements.

<html>
  <body>
   <div class="container">
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
    <div class="block"></div>
   </div>
    <button id="button">Generate!</button>
  </body>
</html>

And then the CSS:

.container {
  display: flex;
}
.block {
  /* display: inline-block; */
  width: 100px;
  height: 140px;
  border: 2px solid;
  visibility: hidden;

  margin-left: 15px;
}

I added a margin-left to all the .block elements, but you can of course set them with the flex display or however you want.

Kevin Williams
  • 186
  • 1
  • 16
0

You can set your .block element to display: none; instead of visibility: hidden; and change your script into this:

 $("button").click(function() {
    let i = 0;
    setInterval(function() {
    if(i == 4) clearInterval();
    $(".block").eq(i).css({display:"inline-block"});
    $(".block").eq(i).html("TEXT" + i);
    i++;
  },100);
});

Fiddle

matthias_h
  • 11,356
  • 9
  • 22
  • 40
  • Thanks for the answer. Do you know why that is? – karirogg Apr 10 '20 at 16:42
  • Unfortunately not. I checked https://stackoverflow.com/questions/133051/what-is-the-difference-between-visibilityhidden-and-displaynone without getting an explanation. There's mentioned that "visibility:hidden hides an element, but it would still take up the same space as before". But it looks like the initial place can be wrong and gets corrected immediately. – matthias_h Apr 10 '20 at 16:53
-1

Here is a working fiddle.

You could set the height to 0 then set it in the interval function.

$("button").click(function() {
    let i = 0;
    setInterval(function() {
    if(i == 4) clearInterval();
    $(".block").eq(i).css({visibility:"visible", height: "140px"});
    $(".block").eq(i).html("TEXT" + i);
    i++;
  },100);
});

With this css:

.block {
  display: inline-block;
  width: 100px;
  height: 0;
  border: 2px solid;
  visibility: hidden;
}

button {
  position: absolute;
  top: 170px;
  left: 50px;
}
twharmon
  • 4,153
  • 5
  • 22
  • 48