CSS遮罩层:hover状态丢失及解决方案

jcwrtfgf 2年前
   <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>