最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - workaround for display block and css transitions not triggering - Stack Overflow

programmeradmin4浏览0评论

Sorry all for so many updates in this question and I'm glad for everyone trying to help me. I think below there is a more clear way to understand this problem:

CSS transitions will not apply if you set the display property of an element to block immediately before changing the property with the transition attached. Consider the following code:

CSS:

#creative {
    display: none;
    opacity: 0;
    transition: opacity 0.5s; 
}

HTML:

<div id="creative">
    <span>Sample text</span>
</div>

Javascript:

var dom = {};
dom.creative = document.getElementById('creative');
dom.creative.style.display = 'block';
dom.creative.style.opacity = 1;

The element will properly show, but without any transition. A workaround for this problem is to force a repaint by accessing one of the visual properties of the element:

dom.creative.style.display = 'block';
var a = dom.creative.offsetHeight; /* <-- forces repaint */
dom.creative.style.opacity = 1;

is there a good way around this? and by good i mean not adding a extra line of javascript code everytime i need to change the display property and a property with a transition attached.

Sorry all for so many updates in this question and I'm glad for everyone trying to help me. I think below there is a more clear way to understand this problem:

CSS transitions will not apply if you set the display property of an element to block immediately before changing the property with the transition attached. Consider the following code:

CSS:

#creative {
    display: none;
    opacity: 0;
    transition: opacity 0.5s; 
}

HTML:

<div id="creative">
    <span>Sample text</span>
</div>

Javascript:

var dom = {};
dom.creative = document.getElementById('creative');
dom.creative.style.display = 'block';
dom.creative.style.opacity = 1;

The element will properly show, but without any transition. A workaround for this problem is to force a repaint by accessing one of the visual properties of the element:

dom.creative.style.display = 'block';
var a = dom.creative.offsetHeight; /* <-- forces repaint */
dom.creative.style.opacity = 1;

is there a good way around this? and by good i mean not adding a extra line of javascript code everytime i need to change the display property and a property with a transition attached.

Share Improve this question edited Jul 5, 2016 at 21:09 inpleteness asked Jul 5, 2016 at 17:33 inpletenessinpleteness 2702 silver badges12 bronze badges 8
  • 1 Very good question! I am curious about this too, and will dig to see what I find... – trusktr Commented Jul 5, 2016 at 17:36
  • 2 transition works where numbers are involved ... it goes step by steps, but not with states like none/block where ... you actually have a single step :) (beside how to apply opacity on display:none element ) – G-Cyrillus Commented Jul 5, 2016 at 17:38
  • Consider changing your question to define objective criteria of what you expect to see in an answer. Right now it appears you're asking for opinions ("best" "more elegant"), which are generally off-topic. – Heretic Monkey Commented Jul 5, 2016 at 17:47
  • 1 It seems like only hacks exist: stackoverflow./questions/8840580, stackoverflow./questions/3485365, and prehensive list by Paul Irish: gist.github./paulirish/5d52fb081b3570c81e3a – trusktr Commented Jul 5, 2016 at 17:49
  • Do you want to repaint or do you want a transition/animation to be re-executed? – Asons Commented Jul 5, 2016 at 18:18
 |  Show 3 more ments

4 Answers 4

Reset to default 2

Based on the code you present in your question I'm going on a pletely different way here, and use animation instead, which will make the whole repaint issue go away

Updated with a script the set the div to display: block

var dom = {};
dom.creative = document.getElementById('creative');
dom.creative.style.display = 'none';

var butt = document.getElementById('button');
butt.addEventListener('click', function(e) {
  
  if (dom.creative.style.display == 'block') {
    dom.creative.style.display = 'none';    
  } else {
    dom.creative.style.display = 'block';
  }

})
#creative {
  display: none;
  opacity: 0;
  animation: opac 1s forwards;
  margin: 20px;
}
@keyframes opac {
  100% {
    opacity: 1;
  }
}
button {
  margin: 20px;
}
<button id="button">Toggle display</button>

<div id="creative">
  <span>Sample text</span>
</div>

If display: none is not needed, one can use transition and simply toggle a class like this

var dom = {};
dom.creative = document.getElementById('creative');

var butt = document.getElementById('button');
butt.addEventListener('click', function(e) {
  
  dom.creative.classList.toggle('show');

})
#creative {
  opacity: 0;
  transition: opacity 1s;
  margin: 20px;
}
#creative.show {
  opacity: 1;
  transition: opacity 1s;
}
button {
  margin: 20px;
}
<button id="button">Toggle display</button>

<div id="creative">
  <span>Sample text</span>
</div>

For transition, besides the offsetHeight and the setTimeout solution, there is a 3:rd, having the same visual effect as toggle display block/none, setting the height/width to 0.

var dom = {};
dom.creative = document.getElementById('creative');

var butt = document.getElementById('button');
butt.addEventListener('click', function(e) {
  
  dom.creative.classList.toggle('show');

})
#creative {
  width: 0;
  height: 0;
  opacity: 0;
  transition: opacity 1s, width 0s 1s;
  margin: 20px 0;
}
#creative.show {
  width: 100%;
  height: auto;
  opacity: 1;
  transition: opacity 1s, width 0s;
}
button {
  margin: 20px 0;
}
<button id="button">Toggle display</button>

<div id="creative">
  <span>Sample text</span>
</div>

<div>Other content</div>

There are no transitions defined for absolute properties, like display. How do you interpolate between none and block? You can't. But you can create some post-functions that will run after the animation is done.

Using setTimeout

You can use setTimeout and execute some code after the animation is over:

ANIMATION_TIME = 0.5 * 1000; // in ms

function show(element) {
  // Display property must be set inmediatly; otherwise, the 'show' animation won't be visible until it ends.
  element.style.display = 'block';
  element.opacity = 1;
}

function hide(element) {
  element.opacity = 0;

  setTimeout(function() {
    element.style.display = 'none';
  }, ANIMATION_TIME);
}

// Call examples
var dom = {};
dom.creative = document.getElementById('creative');
show(dom.creative);
hide(dom.creative);

Using animation events

As @trustk has pointed out, you can also (and preferably) use DOM events:

function show(element) {
  element.style.display = 'block';
  element.opacity = 1;
  element.removeEventListener("animationend", afterHide, false);
}

function afterHide(e) {
  // e.target -> element
  e.target.display = 'none';
}

function hide(element) {
  element.opacity = 0;
  element.addEventListener("animationend", afterHide, false);
}

If you just add a timeout to the opacity call it should work! The duration can be almost nothing, as long as the display and opacity properties don't get called at the same time.

var dom = {};
dom.creative = document.getElementById('creative');
dom.creative.style.display = 'block';
setTimeout(function(){
  dom.creative.style.opacity = 1;
},1);

You can also use a jQuery fadein, if jQuery is an option. Then you can leave out the opacity altogether and just call it like...

dom.creative.fadeIn(500)

You can show / hide element without javascript using css transition only.

<!--html-->
<div id="parent">Some parent text
    <div id="creative">Some details</div>
</div>

/*css*/
#parent #creative{
   opacity:0;
   visibility:hidden;
   clip: rect(0px,0px,0px,0px);
   transition: all 0.5s;
}
#parent:hover #creative{/*trigger transition*/
   opacity:1;
   visibility:visible;
   clip: rect(0px,220px,0px,330px);
}
发布评论

评论列表(0)

  1. 暂无评论