Skip to content
Advertisement

Why does displaying text inside the document via transform: translateY(-100%) create extra white-space at bottom of page?

Put very briefly

I wrote a vuejs component that includes a small icon that displays some text when being hovered over.
I created a minimal example here: https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136

The special thing is: when the icon is at the very bottom of the page the text is displayed above the icon instead of below such that the page does not have to be extended. However, even when the text is displayed above the icon, the page is extended without any content (even the html ends before the whitespace). I tested this in Firefox and Chromium.

I would like to understand where this white-space comes from and how to get rid of it.

I will explain in more detail what I’m trying to do below.
If my entire approach to this does not make much sense I’m also happy for suggestions.

My approach so far

First step: display text when hovering an image

What I want to achieve seems quite straight forward with vuejs at first glance. I want to have a vue component that shows an image (for example a small info icon). When one hovers over the image with the mouse a text is displayed that overlays the other content on the page.

This can be achieved in different ways – for example: Create a component that has a hovered variable like so

<script>
export default {
  name: 'ShowBox',
  props: ['showText'],
  data() {
    return {
      hovered: false,
    }
  },
}

</script>

In the template, bind the class of the to be shown text to the hovered variable and handle an @mouseenter and @mouseleave event in the image accordingly like so

<template>
  <div class="show-box">
    <div class="show-icon">
      <img width="50px" @mouseenter="hovered = true" @mouseleave="hovered = false" src="...">
    </div>
    <div class="show-text" v-bind:class="{'hide': !hovered}">
      {{ showText }}
    </div>
  </div>
</template>

Finally, in the style the class show-box should be positioned and the class show-text should include a position: absolute such that it overlays the text. The class hide should somehow hide the text – for example with opacity: 0.

So far so good – for sure one can add a lot of stuff to make this more decent but for the sake of the example let’s keep it simple. The result might look as this (link to picture).

Second step: display image above the icon when at the bottom of the page

Let’s imagine the icon is at the very bottom of the page. If one then hovers the icon the page might have to be extended in order to display the full text. I want to avoid this and have the text displayed above the icon instead.

For this I took the following steps:

I wrapped the text in an extra div. The text is now not rendered anymore when the page loads but only when one hovers over the image though a display: none in the style.

<template>
  <div class="show-box">
    <div class="show-icon">
       <img width="50px" @mouseenter="hovered = true, , textRendered = true" @mouseleave="hovered = false" src="...">
    </div>
    <div class="show-text-wrap">
      <div class="show-text" v-bind:class="{'not-rendered': !textRendered, 'hide': !hovered, 'render-top': renderTop}">
        {{ showText }}
      </div>
    </div>
  </div>
</template>

Furthermore, I included a life-cycle hook mounted such that after the page is rendered it is checked, whether there is enough space below the show-text-wrap div to display the text. If not, a variable renderTop = true is set. The style of the show-text div is now also bound to the renderTop variable such that if it is set (in the case of insufficient space at the botton of the page) the image is rendered above the icon (also see template above). The mounted hook looks like

mounted() {
    this.$nextTick(function () { //to be sure that page is fully rendered
          var el = this.$el.lastElementChild; //show-text-wrap element
          var rect = el.getBoundingClientRect();
          if (rect.bottom + window.pageYOffset + 300 > (document.body.clientHeight)) {
            this.renderTop = true;
          }
        }
    )
  },

Finally, the render-top class simply translates the text to the top.

.render-top{
  transform: translateY(-100%);
}

The result when hovering over an icon at the bottom of the page might look like this (link to picture).

The problem

This works fine in principle. The only problem is: when the text is moved to the top as intended, some extra unwanted white-space appears at the bottom of the page (tested in Firefox and Chromium). The white-space interestingly extends the html document.
I don’t understand where this comes from and want to get rid of this since the whole point of moving the displayed text to the top was to avoid the page being extended.

In this (link to image) one can see the extra white-space that extends beyond the html document.

I also tried to move the text above the icon by different means for example

.render-top{
  bottom: 0;
}

or even with hard coding pixel numbers like

.render-top{
  bottom: 100px;
}

but none of these solve the problem.

I created a working minimal example for this https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136 where the problem can also be seen.

Any help is greatly appreciated – many thanks in advance!

Advertisement

Answer

A relative positioned element behaves as if it hadn’t been moved

By now I found out the answer myself. I will post it in case someone finds this useful.

The goal was to always move the text-box inside the viewport. However, in the style the show-box’s position was defined as position: relative; and only its direct parent show-text-wrap is absolutely positioned.

According to https://developer.mozilla.org/en-US/docs/Web/CSS/position a relative positioned element is offset relative to its original position. This will not change the position of any other elements in the document.
This means that the layout of the document will behave as if the element was still at its original position.
Since the <show-box> originally reached beyond the <html> element such that the page was extended this is still the case regardless of the fact that the element appears moved into the document.

So in order to fix the problem change

.show-text {
  position: relative;
}

to

.show-text {
  position: absolute;
}
Advertisement