JS效果之《獲取鼠標所在區域的容器,區域外都遮罩半透明》

昨天朋友說要實現一個效果,獲取鼠標所在區域的容器,區域外都遮罩半透明,起名爲專一模式。咱們今天來實現一下。
image.png前端

需求分析

  1. 獲取鼠標位置,鼠標屬於的容器。mouseovermouseoutmousedownmouseupmouseentermouseleavemousemove 這麼多事件太容易了。node

    1. 可是由於 DOM 結構太多了,其實要的元素有多是父級祖先級,這裏須要實現一個相似於 jquery 的 parants。
    2. elementsFromPoint 獲取座標下的 DOM 元素
    3. e.path 獲取路徑
  2. 區域外遮罩半透明。這個感受相似於新手引導的那種效果。jquery

    1. 方案:克隆節點,fixed定位
    2. 方案:box-shadow
    3. 方案:outline
    4. 方案:border,這個就不推薦使用了,上面兩個不會影響位置。

功能實現

獲取鼠標位置的DOM元素

簡直不要太簡單。經過事件對象直接拿 e.target微信

window.addEventListener('mousemove', function(e){
    console.log(e.target)
})

向上遞歸查找符合條件的DOM元素

parentNode 能夠獲取到父節點,由於只有一個父節點,比找子節點還少了一個遍歷。
實現了一個簡單的選擇器,只處理了單個的 class、id、tag,想深刻的能夠看 jquery 的實現。app

function findParentAttribute(node, key = ''){
    key = key.trim();
    if(!node) return null;
    if(!key) return node;
    if(node == document) return null;
    switch(key.slice(0,1)){
        case '.': if(node.classList.contains(key.slice(1))){return node}
        case '#': if(node.id == key.slice(1)){return node}
        default: if(node.tagName.toLowerCase() == key){return node}
    }
    if(node.parentNode){
        return findParentAttribute(node.parentNode, key)
    }
    return null;
}

經過 elementsFromPoint 來實現獲取鼠標位置DOM 節點

通過評論區的哥們提醒,能夠用 document.elementsFromPoint(e.clientX, e.clientY) 來獲取全部的,就不用遞歸了dom

經過 e.path 來實現獲取鼠標位置 DOM 節點

忽然又想到可使用 e.path 來直接獲取觸發路徑,省去遞歸。佈局

遮罩半透明實現

outline 實現

image.png

box-shadow

image.png

JS 克隆 DOM

由於原始的 DOM 不能直接修改成 fixed,會形成佈局變化,因此咱們直接克隆一個,而後將克隆的 fixed 定位。相似於模擬拖拽效果代碼。spa

這個懶得寫了,有沒有大佬評論區留言呀3d

完整代碼

shadowClass = ['.stream-list__item','div','#app'];
shadowEl = null;
shadowStyleTimeout = 0;
shadowStyle = `.lilnong-shadow{outline: 9999px solid rgba(0,0,0,.5);z-index: 9999999999;transform: translate3d(0px,0px,1px);position: relative;}`;
if(!window.styleEl){
    var styleEl = document.createElement('style');
    styleEl.id = styleEl;
}
styleEl.innerHTML = shadowStyle;
if(!styleEl.parentNode){
    document.head.appendChild(styleEl)
}


window.addEventListener('mouseover', function(e){
    var el = e.target;
    var newEl = null;
    for(let i = 0,l = shadowClass.length; i < l; i++){
        newEl = findParentAttribute(el, shadowClass[i]);
        if(newEl) break;
    }
    if(shadowEl) shadowEl.classList.remove('lilnong-shadow')
    clearTimeout(shadowStyleTimeout);
    shadowStyleTimeout = setTimeout(v=>{
        if(newEl){
            newEl.classList.add('lilnong-shadow')
            shadowEl = newEl;
        }
    },50)
})

function findParentAttribute(node, key = ''){
    //console.log(node, key)
    key = key.trim() || '';
    if(!node) return null;
    if(!key) return node;
    if(node == document) return null;
    switch(key.slice(0,1)){
        case '.': if(node.classList.contains(key.slice(1))){return node}
        case '#': if(node.id == key.slice(1)){return node}
        default: if(node.tagName.toLowerCase() == key){return node}
    }
    if(node.parentNode){
        return findParentAttribute(node.parentNode, key)
    }
    return null;
}

image.png

基於 elementsFromPoint 來獲取 dom

評論區一哥們提醒還有 elementsFromPointelementFromPoint 來實現,獲取鼠標位置的 DOM 元素code

shadowClass = ['.stream-list__item','div','#app'];
shadowEl = null;
shadowStyleTimeout = 0;
shadowStyle = `.lilnong-shadow{outline: 9999px solid rgba(0,0,0,.5);z-index: 9999999999;transform: translate3d(0px,0px,1px);position: relative;}`;
if(!window.styleEl){
    var styleEl = document.createElement('style');
    styleEl.id = styleEl;
}
styleEl.innerHTML = shadowStyle;
if(!styleEl.parentNode){
    document.head.appendChild(styleEl)
}


window.addEventListener('mouseover', function(e){
    if(shadowEl) shadowEl.classList.remove('lilnong-shadow')
    var els = document.elementsFromPoint(e.clientX, e.clientY);
    shadowClass.every(selectorKey=>{
        var el = els.find(el=>{
            keySlice = [selectorKey.slice(0,1),selectorKey.slice(1)]
            switch(keySlice[0]){
                case '.': if(el.classList.contains(keySlice[1])){return el}
                case '#': if(el.id == keySlice[1]){return el}
                default: if(el.tagName.toLowerCase() == selectorKey){return el}
            }
            return false;
        })
        if(el){
             el.classList.add('lilnong-shadow')
             shadowEl = el;
             return false
        }
        return true
    })
})

微信公衆號:前端linong

clipboard.png

相關文章
相關標籤/搜索