Reputation: 3080
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
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
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
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 span
s. 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