karirogg
karirogg

Reputation: 128

Divs incorrect placement after showing in intervals (fiddle included)

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!

Upvotes: 0

Views: 55

Answers (5)

royranger
royranger

Reputation: 374

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.

Upvotes: 1

epascarello
epascarello

Reputation: 207527

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>

Upvotes: 1

matthias_h
matthias_h

Reputation: 11416

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

Upvotes: 0

Kevin Williams
Kevin Williams

Reputation: 196

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.

Upvotes: 0

twharmon
twharmon

Reputation: 4282

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;
}

Upvotes: -1

Related Questions