document.elementFromPoint

简介:

先说一下这个方法的参数

elemntFromPoint(x,y);//传入坐标值,返回当前页面上包含该坐标点的顶层元素

注意2点,坐标值和顶层元素

先说坐标,因为不同的人理解是不一样的,也就造就了这个方法在不同的浏览器中表现是不一样的,所以在传入坐标时就分 整体页面坐标 和 可视区域坐标,我们看上篇文章中的图来理解下:
pic
中间的方块是可视区域,红点相对可视区域的左上角我们称之为 clientX和clientY,相对于页面起始处的左上角称之为 pageX和pageY

有的浏览器在调用elementFromPoint时要求传入clientX和clientY而有的要求pageX和pageY,具体的详见:http://www.quirksmode.org/webkit.html 看 elementFromPoint部分,上面也提到如果在标准中进行规则统一,也会规定使用可视区域的坐标

显然我们如果要用这个方法时,就要注意兼容了,而对于同一种浏览器,因为版本的不同,导致同样的这个方法可能要求传入的坐标也不同,比如chrome浏览器,那么,我们该如何去兼容呢?

The w3c specification says:

The elementFromPoint(x, y) method, when invoked, must return the element at coordinates x,y in the viewport. The element to be returned is determined through hit testing. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any), or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), the method must return null. If there is no element at the given position the method must return the root element, if any, or null otherwise.

调用elementFromPoint时,必须传入可视区域内的坐标,如果你传入的坐标不在可视区域内,即使在这个坐标处有元素,也将返回null;我们可以根据这个特性来写兼容代码:

使用jq实现的代码如下,我们可以很方便的改写成自已的

(function($) {
    var check = false,
        isRelative = true;
    $.elementFromPoint = function(x, y) {
        if (!document.elementFromPoint) return null;
        if (!check) {
            var sl;
            if ((sl = $(document).scrollTop()) > 0) {
                isRelative = (document.elementFromPoint(0, sl + $(window).height() - 1) == null);
            } else if ((sl = $(document).scrollLeft()) > 0) {
                isRelative = (document.elementFromPoint(sl + $(window).width() - 1, 0) == null);
            }
            check = (sl > 0);
        }
        if (!isRelative) {
            x += $(document).scrollLeft();
            y += $(document).scrollTop();
        }
        return document.elementFromPoint(x, y);
    }
})(jQuery);

原理就是上面说的,如果页面有滚动,则尝试获取页面坐标最边缘处的元素,如果能获取到,说明是使用页面坐标,因为在有滚动的情况下,获取可视边缘处的坐标,页面坐标会大于可视区域的坐标,所以如果是用可视区域坐标,肯定返回null

嗯,一切看起来都还不错,IE6 7就不行了,仔细看了下这个方法,这个方法最早应该是IE特有的,最后被其它浏览器实现,在IE下,一直使用的是可视区域的坐标,但是在IE6 7的情况下,当你传大于可视区域的坐标时,也是可以获取到值的,也就造成了上面兼容代码无法在IE6 7下正常工作,所以上面的代码并没有考虑IE6 7的情况,需要你添加一些判断。

除了坐标问题外,该方法只能返回顶层元素,也就是在有2个元素重叠的情况下,只能返回最上层的元素。

之前我也有写过高效拖动的文章,比如拖动排序分类,常见的是鼠标在拖动的时候,不停的计算鼠标是在哪个分类上面,然后做出变换的效果,如果列表元素比较少的情况下还是可以的,如果大于1000个,而这些分类的高度不定,通过这个循环判断的方法显示就会觉得很卡

我在之前的文章中提到可以在mousemove时,不让任何元素挡着鼠标(通常我们可能会在拖动时,让被拖动的元素随鼠标一起移动,这时候元素可能挡在鼠标的下方),可通过事件的event.target获取鼠标指向的元素,这样列表中有多少个元素都不会影响到效果

回头说一下setCapture方法,该方法是IE下让元素捕获到事件,比如鼠标移到浏览器外边也可以响应得到,然而这个方法在firefox在4版本时被引进firefox中,引入后如果拖动时你调用了setCapture,就算鼠标下面没有东西挡着,event.target也始终是鼠标按下时的target,IE中则不是,调用setCapture后,mousemove时依然可以获取到,当然IE下可能要用event.srcElement获取

所以,保险起见,我们要调用setCapture,而调用后,firefox4以后又不能及时获取到鼠标下的元素,抛开这些,在ios设备上,touchmouse时,同样也是获取不到手指指向的元素,所以只好回头折腾这个elementFromPoint了。

在折腾这些的时候,还发现诸如getBoundingClientRect在ios设备上也是有问题的,这个方法正常返回相对可视区域的坐标,而在ios上,实现的却是相对页面的坐标,实在让人郁闷

回头再看一下兼容,之前做浏览器间的兼容,更多的是这个浏览器有这个方法,那个浏览器有那个方法,方法的不同而已,现在的兼容则是同样的方法,最终的结果不同,比如setCapture还有getBoundingClientRect等,可能还有其它一些有问题的方法,这种兼容起来更麻烦,远不如没有方法来的干脆些。

目录
相关文章
|
移动开发 JavaScript 前端开发
分享88个JavaScript源码,总有一款适合您
分享88个JavaScript源码,总有一款适合您
578 0
|
人工智能 Python
Python 反编译:pyinstxtractor工具和uncompyle6库的使用
上期我们介绍了库的使用方法,已经可以将.py文件编译成.exe文件运行了,这期博客,我们将教大家如何将一个编译出的.exe文件反编译出源文件。
4783 0
Python 反编译:pyinstxtractor工具和uncompyle6库的使用
|
JavaScript
ant design vue 设置表格选择框,全选按钮选不全
ant design vue 设置表格选择框,全选按钮选不全
1831 0
|
并行计算 API 数据处理
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
|
安全 编译器 Go
Go runtime 调度器精讲(十):异步抢占
Go runtime 调度器精讲(十):异步抢占
|
前端开发 JavaScript
文本,wangEditor5展示HTML无样式,wangEditor5如何看源码,Ctrl + U看CSS文件,代码高亮,Prism.js可以实现,解决方法,参考网页源代码的写法
文本,wangEditor5展示HTML无样式,wangEditor5如何看源码,Ctrl + U看CSS文件,代码高亮,Prism.js可以实现,解决方法,参考网页源代码的写法
|
jenkins Java 测试技术
Jenkins 在持续集成/持续交付(CI/CD)管道中的应用
【8月更文第31天】 在现代软件开发过程中,持续集成(Continuous Integration, CI)和持续交付(Continuous Delivery, CD)已经成为提升开发效率和软件质量的重要实践。Jenkins 是一个广泛使用的开源工具,它能够帮助团队实现自动化构建、测试和部署,是 CI/CD 流水线的核心组件之一。本文将详细介绍 Jenkins 在 CI/CD 管道中的应用,并提供具体的代码示例。
516 0
|
Prometheus 网络协议 应用服务中间件
部署基于etcd的coredns集群
部署基于etcd的coredns集群
293 0
|
SQL 存储 Java
一文帮你搞定MyBatis的类型转换模块,深度好文,欢迎一键三连!!!
MyBatis是一个持久层框架ORM框架,实现数据库中数据和Java对象中的属性的双向映射,那么不可避免的就会碰到类型转换的问题,在PreparedStatement为SQL语句绑定参数时,需要从Java类型转换为JDBC类型,而从结果集中获取数据时,则需要从JDBC类型转换为Java类型,所以我们来看下在MyBatis中是如何实现类型的转换的。
一文帮你搞定MyBatis的类型转换模块,深度好文,欢迎一键三连!!!