JS效果之《获取鼠标所在区域的容器,区域外都遮罩半透明》

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 昨天朋友说要实现一个效果,获取鼠标所在区域的容器,区域外都遮罩半透明,起名为专注模式。我们今天来实现一下。

bVbO7Do.webp.jpg


需求分析


  1. 获取鼠标位置,鼠标属于的容器。mouseovermouseoutmousedownmouseupmouseentermouseleavemousemove这么多事件太容易了。


a.但是因为 DOM 结构太多了,其实要的元素有可能是父级祖先级,这里需要实现一个类似于 jquery 的 parants。


b.elementsFromPoint 获取坐标下的 DOM 元素


c.e.path 获取路径


  1. 区域外遮罩半透明。这个感觉类似于新手引导的那种效果。


a.方案:克隆节点,fixed定位


b.方案:box-shadow


c.方案:outline


d.方案:border,这个就不推荐使用了,上面两个不会影响位置。


功能实现


获取鼠标位置的DOM元素


简直不要太简单。通过事件对象直接拿 e.target


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


向上递归查找符合条件的DOM元素


parentNode 可以获取到父节点,因为只有一个父节点,比找子节点还少了一个遍历。

实现了一个简单的选择器,只处理了单个的 class、id、tag,想深入的可以看 jquery 的实现。


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) 来获取所有的,就不用递归了


通过 e.path 来实现获取鼠标位置 DOM 节点


突然又想到可以使用 e.path 来直接获取触发路径,省去递归。


遮罩半透明实现


outline 实现


bVbO7Hy.webp.jpg


box-shadow


bVbO7Iq.webp.jpg


JS 克隆 DOM


因为原始的 DOM 不能直接修改为 fixed,会造成布局变化,所以我们直接克隆一个,然后将克隆的 fixed 定位。类似于模拟拖拽效果代码。


完整代码


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


bVbO7HR.webp.jpg


基于 elementsFromPoint 来获取 dom


评论区一哥们提醒还有 elementsFromPointelementFromPoint 来实现,获取鼠标位置的 DOM 元素


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


相关文章
|
27天前
|
JavaScript 前端开发
JavaScript实现鼠标放上去之后高亮显示且隔行换色
JavaScript实现鼠标放上去之后高亮显示且隔行换色
12 0
|
6月前
|
Web App开发 移动开发 JavaScript
【前端用法】HTML5 Video标签如何屏蔽右键视频另存为的js代码以及如何禁用浏览器控件,Video 禁止鼠标右键下载
【前端用法】HTML5 Video标签如何屏蔽右键视频另存为的js代码以及如何禁用浏览器控件,Video 禁止鼠标右键下载
168 0
|
1月前
|
存储 JSON JavaScript
如何使用 JavaScript 代码创建虚拟鼠标点击事件
如何使用 JavaScript 代码创建虚拟鼠标点击事件
18 0
|
2月前
|
JavaScript NoSQL Redis
深入浅出:使用 Docker 容器化部署 Node.js 应用
在当今快速发展的软件开发领域,Docker 作为一种开源的容器化技术,已经成为了提高应用部署效率、实现环境一致性和便于维护的关键工具。本文将通过一个简单的 Node.js 应用示例,引导读者从零开始学习如何使用 Docker 容器化技术来部署应用。我们不仅会介绍 Docker 的基本概念和操作,还会探讨如何构建高效的 Docker 镜像,并通过 Docker Compose 管理多容器应用。此外,文章还将涉及到一些最佳实践,帮助读者更好地理解和应用 Docker 在日常开发和部署中的强大功能。
64 0
|
2月前
|
运维 JavaScript 开发者
深入浅出:使用Docker容器化部署Node.js应用
在当今快速发展的软件开发领域,构建一套高效、可靠且易于扩展的开发环境成为了许多开发者和企业的首要任务。本文将探讨如何利用Docker这一强大的容器化技术,实现对Node.js应用的快速部署和管理。不同于传统的摘要方式,我们将通过一个实际操作的视角,逐步引导读者理解Docker的基本概念、容器与镜像的区别、以及如何构建自己的Node.js应用Docker镜像,最终实现应用的容器化部署。此外,文章还将简要介绍Docker Compose的使用,帮助读者管理包含多个服务的复杂应用。无论是刚接触Docker的新手,还是希望深化理解容器化技术的资深开发者,本文都将提供有价值的见解。
37 0
|
4月前
|
JavaScript
【经典】原生JS实现“用鼠标拖拽(drag)内容DIV”,滚动条对应同步滚动
【经典】原生JS实现“用鼠标拖拽(drag)内容DIV”,滚动条对应同步滚动
|
4月前
|
JavaScript 前端开发 API
js实现容器之间交换
js实现容器之间交换
59 0
|
4月前
|
JavaScript 前端开发
原生JavaScript之dom表单改变和鼠标常用事件
那么好,本次我们聊聊表单改变时如何利用onchange方法来触发input改变事件以及鼠标常用的滑入滑出,点击down和点击up事件.
30 2
|
5月前
|
JavaScript 前端开发
JS javascript 点击鼠标 鼠标事件 获取元素 获取元素Xpath
JS javascript 点击鼠标 鼠标事件 获取元素 获取元素Xpath
58 0
|
5月前
|
JavaScript
网页里如何使用js屏蔽鼠标右击事件
网页里如何使用js屏蔽鼠标右击事件
29 0