Home » vue » Vue and CSS3: absolute position in a relative wrapper messes up height

Vue and CSS3: absolute position in a relative wrapper messes up height

Posted by: admin November 26, 2021 Leave a comment

Questions:

I am new to Vue and am learning the intricacies of CSS3

I am building a component which can be found here: https://codesandbox.io/s/yjp674ppxj

In short, I have a ul element with relative positioning and then a list of div elements with absolute positioning and I dynamically calculate the top property.

Ideally, I would like to have text appear after the entire list. However, by doing this, the ul element now has a height of 0px. How can I preserve height and maintain the same effect?

below is a pure html5 & css3 MWE:

ul {
  position: relative;
  border: solid 1px coral;
}

li {
  position: absolute;
  top: calc(10px * var(--index));
  border: solid 0.5px black;
}
<ul>
<li style="--index:0">list item</li>
<li style="--index:1">list item</li>
<li style="--index:2">list item</li>
<li style="--index:3">list item</li>
<li style="--index:4">list item</li>

</ul>

<p>test that shoudl be after list</p>

Answers:

Instead of position:absolute you can consider margin-top

ul {
  position: relative;
  border: solid 1px coral;
  padding-top:8px;
}

li {
  margin-top: -8px;
  border: solid 0.5px black;
  transition:0.5s all;
}
li:hover {
  margin-top:-12px;
  margin-bottom:4px;
}
<ul>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>

</ul>

<p>test that shoudl be after list</p>

UPDATE

You can also consider transform on hover:

ul {
  position: relative;
  border: solid 1px coral;
  padding-top:8px;
}

li {
  margin-top: -8px;
  border: solid 0.5px black;
  transition:0.5s all;
}
li:hover {
  transform:translateY(-5px);
}
<ul>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>
  <li>list item</li>

</ul>

<p>test that shoudl be after list</p>

###

If you want to keep position: absolute you can do the following:

Update your ListPerspective.vue:

  <ul class="mx-auto">
    <ListPerspectiveItem
      v-for="(alert, index) in alerts"
      :key="alert.id"
      :index="index"
      :alert="alert"
      :totalElements="alerts.length"
      :verticalShift="15"
      @close="remove(index)"
    />
    <div :style="notificationMsg"><slot></slot></div>
  </ul>

By adding a slotted list element at the end. Also add a computed style, i.e:

  data() {
    return { verticalShift: 15 }
  },
  computed: {
    notificationMsg() {
      return `position: absolute; top: ${(this.alerts.length + 1) * this.verticalShift}px; margin-top: 20px;`;
    }
  }

You calculate total distance from top of the component you need to display your message below notifications.

This unfortunately requires you to define verticalShift in your parent (ListPerspective) component and pass it as a prop to ListPerspectiveItem. If I were you I’d create a simple store with basic configs like these and instead of hardcoding 15 as a default value in your ListPerspectiveItem I’d fetch it from store, doing the same thing in ListPerspective component, i.e.:

data() { 
// generally you should bind store state properties as computed properties, 
// but since it's a default, constant variable you shouldn't have to watch for it's changes
  return { verticalShift: this.$store.state.defaults.css.verticalShift }
}


verticalShift: {
  type: Number, default: this.$store.state.defaults.css.verticalShift
}

Last thing left to do is to update your App.vue by adding your message to the slot:

  <ListPerspective>
      Click the &times; button to remove a message
  </ListPerspective>

Example