Roy Prins
Roy Prins

Reputation: 3080

Width of absolutely positioned elements is not respected

I want to do some text replacement, where one substring fades in and the other fades out. My thinking is to have to "overlapping" spans, and animate the opacity.

    <p>
      This is the 
      <span style="position: relative;">
        <span style="position: absolute;">wrong</span>
        <span style="position: absolute;" class="hidden">correct</span>
      </span> 
      answer.
    </p>

I expected wrong and correct to overlap and the surrounding <span> element to have the width of the longest word. But the surrounding element gets a width of 0, causing the word answer to overlap as well.

Is there a workaround to respect the width of absolutely positioned elements? Assume them to have dynamic content, so you cannot be certain which one will be longest.

edit: After some looking around, this seems to be the case: the span with position: relative has no content, so it will have no width. The inner spans will automatically become blocks, because they are positioned absolutely. You could give them a width, but that will not impact the outer span. I think.

Upvotes: 0

Views: 124

Answers (3)

Ihsan S
Ihsan S

Reputation: 54

Updated snippet

var el = document.querySelector('.wrapper'),
correct = document.querySelector('.correct'),
wrong = document.querySelector('.wrong'),
cW = correct.offsetWidth,
wW = wrong.offsetWidth,
btn = document.querySelector('.btn')

correct.style.position = "absolute";
correct.style.top = 0;
wrong.style.position = "absolute";
wrong.style.top = 0;

function checklonger () {
if (el.classList.contains('show-correct')) {
     el.style.width= cW + 'px';
}
else {
     el.style.width= wW + 'px';
}
}
checklonger ();

btn.addEventListener('click', function() {
   el.classList.toggle('show-correct');
   el.classList.toggle('show-wrong');
   checklonger();
});
p {
  position:relative;
}
.wrapper {
  display: inline-block;
  transition: all 1s ease;

}
.correct, .wrong {
  opacity:0;
  transition: all 1s ease;
}
.show-correct .correct, .show-wrong .wrong  {
    opacity:1;
}
<p>
      This is the
      <span class='wrapper show-correct'>
        <span class="correct">correct</span>
        <span class="wrong">wrong </span>

      </span> 
    answer
    </p>

<button class="btn">toggle</button>

Upvotes: 0

vals
vals

Reputation: 64164

Make the container span an inline-flex, direction column

This will stack vertically the inner spans

then make those height 0, so that they share the same vertical position:

.container {
    display: inline-flex;
    flex-direction: column;
}

.item {
    height: 0px;
}
<p>
      This is the 
      <span class="container">
        <span class="item">wrong</span>
        <span class="item hidden">correct long</span>
      </span> 
      answer.
    </p>

Upvotes: 2

Turnip
Turnip

Reputation: 36642

Using position: absolute removes an element from the normal document flow. The element will no longer effect the layout of other elements in the document. The element will take up no vertical or horizontal space which is what you are seeing when the text node answer is overlaid by the absolutely positioned elements.

One solution to this would be to only use position: absolute on the shortest of the two spans. You could then hide or show the correct span by changing the opacity of the elements:

var el = document.querySelector('.result-wrap');

document.querySelector('button').addEventListener('click', function() {
    el.classList.toggle('show-negative');
    el.classList.toggle('show-positive');
});
.result-wrap {
  position: relative;
}

.result {
  opacity: 0;
  transition: all 1s ease;
}

.result-negative {
  position: absolute;
  left: 0;
}

.show-negative .result-negative,
.show-positive .result-positive{
  opacity: 1;
}

.show-negative .result-positive
.show-positive .result-negative{
  opacity: 0;
}
<p>
  This is the 
  <span class="result-wrap show-positive">
    <span class="result result-negative">wrong</span>
    <span class="result result-positive">correct</span>
  </span> 
  answer.
</p>

<button type="button">
Toggle result
</button>

Upvotes: 2

Related Questions