CSS遮罩層:hover狀態丟失及解決方案

jcwrtfgf 7年前發布 | 30K 次閱讀 CSS 前端技術

CSS遮罩層,顧名思義就是在div上,再“鋪”一層半透明的div。在hover時,亦可進一步改變該遮罩層的色彩和透明度。我們可以通過css定位和背景色實現。

CSS遮罩層實現及hover狀態丟失問題

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);
        }

Html代碼:

<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>

普通狀態下的效果:

鼠標Hover時的效果圖:

問題是,在鼠標hover的時候多次快速點擊鼠標,會導致hover狀態失效。這個問題在windows的瀏覽器(包括windows版本的Chrome, FireFox)時常發生,盡管在macOs的各種瀏覽器挺少發生。

Hover狀態丟失的簡單解決方案

基本思路是,點擊鼠標時給div添加isActive class,強制它顯示Hover里的樣式。在鼠標不斷點擊以致于丟失hover時,也會因為添加了isActive class而照樣顯示hover里的樣式。

/*.isActive 擁有:hover相同的樣式*/
  .block:hover .block__overlay,
        .block.isActive .block__overlay {
            background-color: rgba(100, 200, 0, .5);
        }

JS文件:

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);

Hover狀態丟失的通用解決方案

若div里有多個定位元素,鼠標在子元素內部向上移動時,雖然鼠標可能依舊在div內部,但是evt.offsetY可能是負數。依照上述簡單方案判斷結果是,鼠標在div外部,就不對了。為此我們需要一種通用的方案。

以下圖效果舉例。我們在div里添加一個紅色:o:?和對勾

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);
        }

HTML代碼:可以看到添加了block__circle.

<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>

在鼠標從紅色圓圈向上移動到圓圈外部 但仍在div內時,offsetY是小于0的。 如果依舊應用簡單方案里的js,就會錯誤地得出鼠標在div外的結論。

為此我們使用toElement屬性,它表示mouse移動到哪個元素。如果該元素是div的子孫元素,我們就認為鼠標還在div內。FireFox的event沒有toElement屬性,我們用getToElement函數解決。

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);

控制臺查看鼠標點擊div后的class:

鼠標移走之后,div的class:

總結

本文介紹了CSS遮罩的簡單實現,以及在鼠標點擊div時如何保持遮罩層的hover 狀態。具體代碼可查看 https://github.com/JackieGe/a...

 

來自:https://segmentfault.com/a/1190000010748472

 

 本文由用戶 jcwrtfgf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!