使用insertBefore
,appendChild()
或insertAdjacentElement()
移动DOM中的元素后,除非经过一段时间,否则不会发生CSS转换。它的表现就好像有关于这些功能的异步。对此有任何记录的解释吗?
如果这方面的某些方面是异步的,那么" 正确"完成后做某事的方法?是否还有其他事件被触发? setTimeout()
?多久?它是否受到客户端系统负载的影响?
以下示例也可以在this pen中找到,但我会在找到解决方案后更新笔。
在示例代码中,有三个承诺应该连续发生。第一个隐藏了一个元素。第二个将元素移动到DOM中的适当位置。然后最后一个再次显示该元素。显示和隐藏元素是通过切换带有hidden
集的CSS max-height: 0
类来完成的。为元素设置了transition: max-height 0.5s
,但是在移动元素后不会立即发生这种转换。如果我跳过移动元素的函数或插入延迟,则问题不会发生。
console.clear()
window.addEventListener('load', () => {
const infoBlock = document.getElementById('info')
const links = document.getElementsByTagName('a')
for (let link of links) {
link.addEventListener('click', e => {
e.preventDefault()
infoBlock.target = e.target
toggleInfo(infoBlock, 'hide')
.then(infoBlock => positionInfo(infoBlock))
.then(infoBlock => toggleInfo(infoBlock, 'show'))
})
}
})
function toggleInfo(infoBlock, action) {
return new Promise((resolve, reject) => {
const isVisible = () => !infoBlock.classList.contains('hidden')
const transitionHandler = e => {
if (e.propertyName !== 'max-height') return
infoBlock.removeEventListener('transitionend', transitionHandler)
resolve(infoBlock)
}
const correctState = (action === 'hide') ? !isVisible() : isVisible()
if (!correctState) {
infoBlock.classList.toggle('hidden')
infoBlock.addEventListener('transitionend', transitionHandler)
} else {
resolve(infoBlock)
}
})
}
function positionInfo(infoBlock) {
return new Promise((resolve, reject) => {
let target = infoBlock.target
let row = target.parentNode
while (!row.classList.contains('row')) row = row.parentNode
let nextRow = row.nextSibling
if (nextRow !== null) {
nextRow.parentNode.insertBefore(infoBlock, nextRow)
} else {
row.parentNode.appendChild(infoBlock)
}
// row.insertAdjacentElement('afterend', infoBlock)
// Doesn't work in Chrome or Firefox
resolve(infoBlock)
// Doesn't work in Chrome, works in Firefox
// window.requestAnimationFrame(resolve.bind(null, infoBlock))
// Works in Chrome, doesn't work in Firefox
// setTimeout(resolve.bind(null, infoBlock), 10)
// Works in Chrome or Firefox... but why?
// setTimeout(resolve.bind(null, infoBlock), 100)
})
}

.row a {
margin: 10px 0;
}
.info {
background: #111;
color: white;
text-align: center;
height: 240px;
max-height: 240px;
transition: max-height 1s;
}
.info.hidden {
max-height: 0;
}

<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet" />
<div id="info" class="row info hidden">
<p class="col-12">info</p>
</div>
<div class="container">
<div class="row" id="row01">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row02">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row03">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row04">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row05">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
&#13;
答案 0 :(得分:1)
为了过渡到工作,元素必须从一个状态转到另一个状态
浏览器通常会等到他们必须实际绘制您的页面,然后重新计算您通过js应用的所有新样式。
在复杂代码中,不同的状态是同步设置的,因此转换不会发生。
为什么等待一段时间可能工作,或者可能不会:当没有指定重新计算时,你不能确定特定浏览器何时会这样做它。
requestAnimationFrame(()=>requestAnimationFrame(callback))
)。为了解决这个问题,在所有浏览器中,并且同步,您可以告诉浏览器在positionInfo()
之前设置最终状态之前,triggering a reflow重新计算元素上应用的当前样式。解决承诺:
console.clear()
window.addEventListener('load', () => {
const infoBlock = document.getElementById('info')
const links = document.getElementsByTagName('a')
for (let link of links) {
link.addEventListener('click', e => {
e.preventDefault()
infoBlock.target = e.target
toggleInfo(infoBlock, 'hide')
.then(infoBlock => positionInfo(infoBlock))
.then(infoBlock => toggleInfo(infoBlock, 'show'))
})
}
})
function toggleInfo(infoBlock, action) {
return new Promise((resolve, reject) => {
const isVisible = () => !infoBlock.classList.contains('hidden')
const transitionHandler = e => {
if (e.propertyName !== 'max-height') return
infoBlock.removeEventListener('transitionend', transitionHandler)
resolve(infoBlock)
}
const correctState = (action === 'hide') ? !isVisible() : isVisible()
if (!correctState) {
infoBlock.classList.toggle('hidden')
infoBlock.addEventListener('transitionend', transitionHandler)
} else {
resolve(infoBlock)
}
})
}
function positionInfo(infoBlock) {
return new Promise((resolve, reject) => {
let target = infoBlock.target
let row = target.parentNode
while (!row.classList.contains('row')) row = row.parentNode
let nextRow = row.nextSibling
if (nextRow !== null) {
nextRow.parentNode.insertBefore(infoBlock, nextRow)
} else {
row.parentNode.appendChild(infoBlock)
}
// trigger a reflow
row.offsetWidth;
resolve(infoBlock)
})
}
&#13;
.row a {
margin: 10px 0;
}
.info {
background: #111;
color: white;
text-align: center;
height: 240px;
max-height: 240px;
transition: max-height 1s;
}
.info.hidden {
max-height: 0;
}
&#13;
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet" />
<div id="info" class="row info hidden">
<p class="col-12">info</p>
</div>
<div class="container">
<div class="row" id="row01">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row02">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row03">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row04">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
<div class="row" id="row05">
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
<a href="#" class="col-4"><img src="https://via.placeholder.com/150x100" alt="placeholder"></a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
&#13;
请注意,您的代码似乎还有其他问题,但我会帮您解决。