idungotnosn idungotnosn - 7 months ago 17
HTML Question

How to horizontally center an absolutely positioned div over its relatively positioned parent?

I have a speech-bubble class that has a dynamic size and is absolutely positioned. I want it to always be horizontally centered over its parent element which is relatively positioned, no matter what the speech-bubble's size is. I want it to look something like this (where the letter 'O' is the parent relatively positioned element):

enter image description here

Currently, it looks like this if I set the

left
attribute to 0:

enter image description here

This is an easy problem to solve if I can guarantee that the size of the bubble is fixed, but it is most definitely not. I can't solve this by setting
width
or
left
or
margin-left
on an element with class
bubble
. It needs to be more robust than that.

I would prefer a css solution if there is one. However, if the only solution is a javascript one, I will consider that as well.

This is what the code looks like. Note that the margins on smart-bubble-wrapper are not really needed, they only exist so that you can see the bubble in the code sample:



.bubble-wrapper {
position: relative;
margin-top: 100px;
margin-left: 100px;
}

.bubble {
display: block;
position: absolute;
padding: 10px;
background: #FFFFFF;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
border: solid 1px #bababf;
box-shadow: 0px 0px 5px #6bcded;
left: 0;
color: #333333;
}

.bubble-bottom {
bottom: 30px;
}

.bubble-middle-after {
left: calc(50% - 8px);
}

.bubble-middle-before {
left: calc(50% - 10px);
}

.bubble-bottom-before {
bottom: -8px;
}

.bubble-bottom-after {
bottom: -12px;
border-width: 12px 12px 0;
border-style: solid;
border-color: #FFFFFF transparent;
}

.bubble-after {
content: '';
position: absolute;
display: block;
}

.bubble-before {
content: '';
position: absolute;
border-style: solid;
border-color: #bababf;
border-width: 13px 13px 0;
background-color: white;
display: block;
box-shadow: 0px 0px 5px #6bcded;
width: 0px;
height: 10px;
z-index: -1;
transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
}

<div class="bubble-wrapper">
O
<div class="bubble bubble-middle bubble-bottom" arrow-horizontal="middle" arrow-vertical="bottom">
<div class="bubble-before bubble-middle-before bubble-bottom-before"></div>
<span class="ng-scope">How do I center this bubble</span>
<div class="bubble-after bubble-middle-after bubble-bottom-after"></div>
</div>
</div>




Answer

You can use

  1. Choose a big enough length x
  2. Set left to some negative value -x
  3. Set right to calc(100% - x)
  4. Assuming x is big enough, the absolutely positioned element will be centered at the left.
  5. But its width will be 2*x. Use width: fit-content or width: max-content to fix this.
  6. But now it's over-constrained. Set margin-left and margin-right to auto to keep the centering.

For example, choosing x = 100%,

left: -100%;
right: 0;
width: fit-content; /* may need vendor prefixes */
margin: 0 auto;

.bubble-wrapper {
  position: relative;
  margin-top: 100px;
  margin-left: 100px;
}
.bubble {
  display: block;
  position: absolute;
  padding: 10px;
  background: #FFFFFF;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  border: solid 1px #bababf;
  box-shadow: 0px 0px 5px #6bcded;
  color: #333333;

  left: -100%;
  right: 0;
  width: -webkit-fit-content;
  width: -moz-fit-content;
  width: fit-content; /* may need vendor prefixes */
  margin: 0 auto;
}
.bubble-bottom {
  bottom: 30px;
}
.bubble-middle-after {
  left: calc(50% - 8px);
}
.bubble-middle-before {
  left: calc(50% - 10px);
}
.bubble-bottom-before {
  bottom: -8px;
}
.bubble-bottom-after {
  bottom: -12px;
  border-width: 12px 12px 0;
  border-style: solid;
  border-color: #FFFFFF transparent;
}
.bubble-after {
  content: '';
  position: absolute;
  display: block;
}
.bubble-before {
  content: '';
  position: absolute;
  border-style: solid;
  border-color: #bababf;
  border-width: 13px 13px 0;
  background-color: white;
  display: block;
  box-shadow: 0px 0px 5px #6bcded;
  width: 0px;
  height: 10px;
  z-index: -1;
  transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
}
<div class="bubble-wrapper">
  O
  <div class="bubble bubble-middle bubble-bottom" arrow-horizontal="middle" arrow-vertical="bottom">
    <div class="bubble-before bubble-middle-before bubble-bottom-before"></div>
    <span class="ng-scope">How do I center this bubble</span>
    <div class="bubble-after bubble-middle-after bubble-bottom-after"></div>
  </div>
</div>

For browsers that don't support width: fit-content, you can add an inline-block inner wrapper, which can be centered with text-align: center.

.bubble-wrapper {
  position: relative;
  margin-top: 100px;
  margin-left: 100px;
}
.bubble {
  display: block;
  position: absolute;
  left: -100%;
  right: 0;
  text-align: center;
  margin: 0 auto;
}
.bubble-inner-wrapper {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  padding: 10px;
  background: #FFFFFF;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  border: solid 1px #bababf;
  box-shadow: 0px 0px 5px #6bcded;
  color: #333333;
}
.bubble-bottom {
  bottom: 30px;
}
.bubble-middle-after {
  left: calc(50% - 8px);
}
.bubble-middle-before {
  left: calc(50% - 10px);
}
.bubble-bottom-before {
  bottom: -8px;
}
.bubble-bottom-after {
  bottom: -12px;
  border-width: 12px 12px 0;
  border-style: solid;
  border-color: #FFFFFF transparent;
}
.bubble-after {
  content: '';
  position: absolute;
  display: block;
}
.bubble-before {
  content: '';
  position: absolute;
  border-style: solid;
  border-color: #bababf;
  border-width: 13px 13px 0;
  background-color: white;
  display: block;
  box-shadow: 0px 0px 5px #6bcded;
  width: 0px;
  height: 10px;
  z-index: -1;
  transform: rotate(45deg);
  -moz-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
}
<div class="bubble-wrapper">
  O
  <div class="bubble bubble-middle bubble-bottom" arrow-horizontal="middle" arrow-vertical="bottom">
    <div class="bubble-inner-wrapper">
      <div class="bubble-before bubble-middle-before bubble-bottom-before"></div>
      <span class="ng-scope">How do I center this bubble</span>
      <div class="bubble-after bubble-middle-after bubble-bottom-after"></div>
    </div>
  </div>
</div>