CSS遮罩层:hover状态丢失及解决方案
jcwrtfgf
7年前
<p>CSS遮罩层,顾名思义就是在div上,再“铺”一层半透明的div。在hover时,亦可进一步改变该遮罩层的色彩和透明度。我们可以通过css定位和背景色实现。</p> <h2>CSS遮罩层实现及hover状态丢失问题</h2> <p>CSS代码:</p> <pre> <code class="language-css">.block { position: relative; top: 100px; left: 100px; display: inline-block; width: 300px; border-radius: 4px; border:1px solid ; } .block__overlay { position: absolute; top:0; left:0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, .3); } .block:hover .block__overlay { background-color: rgba(100, 200, 0, .5); }</code></pre> <p>Html代码:</p> <pre> <code class="language-css"><div class="block"> <p> 在Mouse hover时,如果快速点击鼠标,可能会丢失mouse hover的效果。这在windows上的浏览器经常出现,造成'闪烁'。虽然在macbook上出现的时候很少。 </p> <p> 解决方案:点击鼠标时,添加isActive 样式,强制显示'hover'里的样式。等mouse out时,去掉isActive class。 </p> <div class="block__overlay"> </div> </div></code></pre> <p>普通状态下的效果:</p> <p><img src="https://simg.open-open.com/show/64eb9e735da7bf3572e263b848f736f0.png"></p> <p>鼠标Hover时的效果图:</p> <p><img src="https://simg.open-open.com/show/d7d7dfb8e8c21af3f73d208575829963.png"></p> <p>问题是,在鼠标hover的时候多次快速点击鼠标,会导致hover状态失效。这个问题在windows的浏览器(包括windows版本的Chrome, FireFox)时常发生,尽管在macOs的各种浏览器挺少发生。</p> <h2>Hover状态丢失的简单解决方案</h2> <p>基本思路是,点击鼠标时给div添加isActive class,强制它显示Hover里的样式。在鼠标不断点击以致于丢失hover时,也会因为添加了isActive class而照样显示hover里的样式。</p> <pre> <code class="language-css">/*.isActive 拥有:hover相同的样式*/ .block:hover .block__overlay, .block.isActive .block__overlay { background-color: rgba(100, 200, 0, .5); }</code></pre> <p>JS文件:</p> <pre> <code class="language-css">var block = document.getElementsByClassName("block")[0]; block.addEventListener('mouseout', function (evt) { // mouse hover时,不断:ideograph_advantage:️快速点击鼠标,可能会触发mouseout事件,尽管并不是真正将鼠标move out了。 // 这里通过offsetX,offsetY来判断鼠标的位置,是否真正还在div内 if (evt.offsetX <= 0 || evt.offsetY <= 0 || evt.offsetX >= block.offsetWidth || evt.offsetY >= block.offsetHeight) { console.log('Really moved out'); if (this.classList.contains('isActive')) { this.classList.remove('isActive'); } } }, false); block.addEventListener('click', function (evt) { if (!this.classList.contains('isActive')) { this.classList.add('isActive'); } }, false);</code></pre> <h2>Hover状态丢失的通用解决方案</h2> <p>若div里有多个定位元素,鼠标在子元素内部向上移动时,虽然鼠标可能依旧在div内部,但是evt.offsetY可能是负数。依照上述简单方案判断结果是,鼠标在div外部,就不对了。为此我们需要一种通用的方案。</p> <p>以下图效果举例。我们在div里添加一个红色:o:️和对勾</p> <p><img src="https://simg.open-open.com/show/04ae2a6cdb6a1d4205aec06129ca08b4.png"></p> <p>CSS代码:</p> <pre> <code class="language-css">.block { position: relative; top: 100px; left: 100px; display: inline-block; width: 300px; border: 1px solid; border-radius: 4px; } .block__overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, .3); } .block:hover .block__overlay, .block.isActive .block__overlay { background-color: rgba(100, 200, 0, .5); } .block__circle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 40px; height: 40px; border: 8px solid red; border-radius: 50%; } .block__circle::after { content: ''; position: absolute; top: 2px; left: 9px; width: 15px; height: 25px; border: 4px solid #eef; border-top: none; border-left: none; -webkit-transform: rotate(30deg); -moz-transform: rotate(30deg); -ms-transform: rotate(30deg); -o-transform: rotate(30deg); transform: rotate(30deg); }</code></pre> <p>HTML代码:可以看到添加了block__circle.</p> <pre> <code class="language-css"><div class="block"> <p> 在Mouse hover时,如果快速点击鼠标,可能会丢失mouse hover的效果。这在windows上的浏览器经常出现,造成'闪烁'。虽然在macbook上出现的时候很少。 </p> <p> 解决方案:点击鼠标时,添加isActive 样式,强制显示'hover'里的样式。等mouse out时,去掉isActive class。 </p> <div class="block__overlay"> </div> <div class="block__circle"> </div> </div></code></pre> <p>在鼠标从红色圆圈向上移动到圆圈外部 但仍在div内时,offsetY是小于0的。 如果依旧应用简单方案里的js,就会错误地得出鼠标在div外的结论。</p> <p>为此我们使用toElement属性,它表示mouse移动到哪个元素。如果该元素是div的子孙元素,我们就认为鼠标还在div内。FireFox的event没有toElement属性,我们用getToElement函数解决。</p> <pre> <code class="language-css">function getToElement(evt) { var node; if (evt.type == 'mouseout') { node = evt.relatedTarget; } else if (evt.type == 'mouseover') { node = evt.target; } if (!node) { return; } while (node.nodeType != 1) { node = node.parentNode; } return node; } HTMLElement.prototype.isChildOf = function (elem) { if (elem && elem.children) { for (var i = 0; i < elem.childElementCount; i++) { var child = elem.children[i]; if (child == this) { return true; } else if (child.childElementCount > 0) { return this.isChildOf(child); } } } return false; } var block = document.getElementsByClassName("block")[0]; block.addEventListener('mouseout', function (evt) { var toElement = evt.toElement || getToElement(evt); if (toElement == this || toElement.isChildOf(this)) { console.log('Does NOT really move out'); } else { console.log('Really moved out'); if (this.classList.contains('isActive')) { this.classList.remove('isActive'); } } /*** * The below code: the old way no long works correctly, because offsetX, offsetY rely on fromElement. * When mouse move up direction out of 'circle', the OffsetY could be negative, but mouse * is still inside the outermost div. */ /* if (evt.offsetX <= 0 || evt.offsetY <= 0 || evt.offsetX >= block.offsetWidth || evt.offsetY >= block.offsetHeight) { console.log('OLD way: Really moved out'); if (this.classList.contains('isActive')) { this.classList.remove('isActive'); } } else { console.log('OLD way: Doest NOT move out'); }*/ }, false); block.addEventListener('click', function (evt) { if (!this.classList.contains('isActive')) { this.classList.add('isActive'); } }, false);</code></pre> <p>控制台查看鼠标点击div后的class:</p> <p><img src="https://simg.open-open.com/show/568a64b6ef9114a1243069947f728cf8.png"></p> <p>鼠标移走之后,div的class:</p> <p><img src="https://simg.open-open.com/show/0b246ab6859a857f2e5fabcca4e6a439.png"></p> <h2>总结</h2> <p>本文介绍了CSS遮罩的简单实现,以及在鼠标点击div时如何保持遮罩层的hover 状态。具体代码可查看 <a href="/misc/goto?guid=4959751486719106985" rel="nofollow,noindex">https://github.com/JackieGe/a...</a></p> <p> </p> <p>来自:https://segmentfault.com/a/1190000010748472</p> <p> </p>