美文网首页
使用原生Javascript实现jQuery offset方法

使用原生Javascript实现jQuery offset方法

作者: nomooo | 来源:发表于2019-05-28 23:50 被阅读0次

    什么是jQuery的offset方法?

    offset() 方法返回或设置匹配元素相对于文档的偏移(位置)。

    看个具体例子,直接上代码

    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        #box1{
            width: 400px;
            height: 400px;
            background-color:lightcoral;
            position: relative;
            left: 10px;
            top: 10px;
        }
        #box2{
            width: 200px;
            height: 200px;
            background-color:lightseagreen;
            position: absolute;
            left: 33px;
            top: 33px;
        }
    </style>
    <body>
        <div id="box1">
            <div id="box2">
                
            </div>
        </div>
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
        <script type="text/javascript">
            console.log($("#box1").offset());//{top: 10, left: 10}
            console.log($("#box2").offset());//{top: 43, left: 43}
        </script>
    </body>
    
    实现效果截图

    使用Javascript实现的思路

    • 递归
    • Element​.get​Bounding​Client​Rect()

    我们通过遍历目标元素、目标元素的父节点、父节点的父节点......依次溯源,并累加这些遍历过的节点相对于其最近祖先节点(且 position 属性非 static)的偏移量,向上直到 document,累加即可得到结果。

    其中,我们需要使用 JavaScript 的 offsetTop 来访问一个 DOM 节点上边框相对离其本身最近、且 position 值为非 static 的祖先元素的垂直偏移量。具体实现为:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>jQuery Tiele</title>
        </head>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            #box1 {
                width: 400px;
                height: 400px;
                background-color: lightcoral;
                position: relative;
                left: 10px;
                top: 10px;
            }
    
            #box2 {
                width: 200px;
                height: 200px;
                background-color: lightseagreen;
                position: absolute;
                left: 33px;
                top: 33px;
            }
        </style>
        <body>
            <div id="box1">
                <div id="box2">
    
                </div>
            </div>
            <script type="text/javascript">
                const offset = ele => {
                    let result = {
                        top: 0,
                        left: 0
                    }
                    /*
                    * nodeType 属性返回以数字值返回指定节点的节点类型。
                    * 如果节点是元素节点,则 nodeType 属性将返回 1。
                    * 如果节点是属性节点,则 nodeType 属性将返回 2。
                    * 如果节点 node.nodeType 类型不是 Element(1),则跳出;
                    * 如果相关节点的 position 属性为 static,则不计入计算,进入下一个节点(其父节点)的递归。
                    * 如果相关属性的 display 属性为 none,则应该直接返回 0 作为结果。
                    */
                    const getOffset = (node, init) => {
                        if (node.nodeType !== 1) {
                            return
                        }
    
                        position = window.getComputedStyle(node)['position']
    
                        if (typeof(init) === 'undefined' && position === 'static') {
                            getOffset(node.parentNode)
                            return
                        }
    
                        result.top = node.offsetTop + result.top - node.scrollTop
                        result.left = node.offsetLeft + result.left - node.scrollLeft
    
                        if (position === 'fixed') {
                            return
                        }
    
                        getOffset(node.parentNode)
                    }
    
                    // 当前 DOM 节点的 display === 'none' 时, 直接返回 {top: 0, left: 0}
                    if (window.getComputedStyle(ele)['display'] === 'none') {
                        return result
                    }
    
                    let position
    
                    getOffset(ele, true)
    
                    return result
    
                }
                let box = document.getElementById('box2')
                let result = offset(box);
                console.log(result)
            </script>
        </body>
    </html>
    
    
    实现截图

    接下来,换一种思路,用一个相对较新的API:getBoundingClientRect来实现jQuery offset方法

    getBoundingClientRect 方法

    返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。
    [图片上传中...(rect.png-6dad39-1559057838375-0)]
    DOMRect 对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。

    rect.png

    代码实现:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>jQuery Tiele</title>
        </head>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            #box1 {
                width: 400px;
                height: 400px;
                background-color: lightcoral;
                position: relative;
                left: 10px;
                top: 10px;
            }
    
            #box2 {
                width: 200px;
                height: 200px;
                background-color: lightseagreen;
                position: absolute;
                left: 33px;
                top: 33px;
            }
        </style>
        <body>
            <div id="box1">
                <div id="box2">
    
                </div>
            </div>
            <script type="text/javascript">
                const offset = ele => {
                    let result = {
                        top: 0,
                        left: 0
                    }
                    // 当前为 IE11以下, 直接返回 {top: 0,left: 0}
                    if (!ele.getClientRects().length) {
                        return result
                    }
    
                    // 当前 DOM 节点的 display === 'none' 时,直接返回 {top: 0,left: 0}
                    if (window.getComputedStyle(ele)['display'] === 'none') {
                        return result
                    }
    
                    result = ele.getBoundingClientRect()
                    // 得到ele所在文档的HTML节点
                    let document = ele.ownerDocument.documentElement
    
                    return {
                        //docElement.clientTop 一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。clientTop 是只读的
                        top: result.top + window.pageYOffset - document.clientTop,
                        left: result.left + window.pageXOffset - document.clientLeft
    
                    }
                }
    
                let box1 = document.getElementById('box1')
                let box2 = document.getElementById('box2')
                let result1 = offset(box1);
                let result2 = offset(box2);
                console.log(result1)
                console.log(result2)
            </script>
        </body>
    </html>
    
    
    实现截图

    需要注意的细节:

    • 第一个
      node.ownerDocument.documentElement

    Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象。

    看个示例

    // 得到p元素所在文档的HTML节点
    d = p.ownerDocument; 
    html = d.documentElement;
    
    • 第二个
      docElement.clientTop

    一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。clientTop 是只读的。

    相关文章

      网友评论

          本文标题:使用原生Javascript实现jQuery offset方法

          本文链接:https://www.haomeiwen.com/subject/oqrmzqtx.html