访问元素位置


CSS 的position属性可以设定不同的值,代表元素的定位方式,如果没有设定,大多数元素默认是static,元素依序绘制在文件的流动版面。还可以设定的值有relativeabsolutefixedinherit

元素的top表示距原点的垂直距离,正值为往下,left表示距原点的水平距离,正值为往右。bottom表示距原点的垂直距离,正值为往上,right表示距原点的水平距离,正值为往左。

absolute表示绝对定位,可以借由设置元素topleftbottomright来定位,虽说名为 absolute,然而定位方式实际上是相对于元素的首个(最接近的)非static定位(非流动)父元素(absolute 的意义应该是指,元素不参与流动配置,浏览器就算改变大小,元素位置看起来都像是绝对位置,也就是不会随着浏览器大小改变而流动变化)。

在绝对定位时,元素可以栈,此时可利用z-index来设定重叠顺序,数值越大,表示可以堆垒在越上面,z-index默认是页面中标签定义的顺序,越后定义的标签z-index越大。

下面这个范例运用了绝对定位、topleftz-index属性,可以使用鼠标点选改变idmessage1<div>的位置,可以按下某个<div>,使之z-index提高,就范例的 HTML 本身来说,<div>元素的第一个非流动父元素就是<html>,因此绝对位置都是指相对于<html>文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">

    <style type="text/css">
        #message1 {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            width: 100px;
            height: 50px;
            padding: 50px;
            margin: 10px;
            position: absolute;
            top: 50px;
            left: 50px;
            z-index: 0;                
        }

        #message2 {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            width: 100px;
            height: 50px;
            padding: 50px;
            margin: 10px;
            position: absolute;
            top: 150px;
            left: 150px;
            z-index: 0;                
        }
    </style>
</head>
<body>

    <div id="message1">这是消息一</div>
    <div id="message2">这是消息二</div>

<script type="text/javascript">
    let message1 = document.getElementById('message1');
    let message2 = document.getElementById('message2');

    document.onclick = function(evt) {
        message1.style.left = `${evt.clientX}px`;
        message1.style.top = `${evt.clientY}px`;
    };

    message1.onclick = function(evt) {
        message1.style.zIndex = 1;
        message2.style.zIndex = 0;
        evt.stopPropagation();
    };

    message2.onclick = function(evt) {
        message2.style.zIndex = 1;
        message1.style.zIndex = 0;
        evt.stopPropagation();
    };

</script>   

</body>
</html>

按此观看结果

relative是指元素基本上仍是流动方式,然而最后会依指定的相对值偏移,相对值是使用topleftbottomright设定。例如,若某元素本来流动方式摆放的位置为 100x50,若设定该元素的positionrelative,而topleft分别设为 20、30 的话,则元素最后的位置就是在 (100+20)x(50+30)= 120x80 的位置。

fixed则是以浏览器视埠(view-port)为基准点。例如,想要让某个元素在卷动后,依旧可以在可视画面中的 100x50 的位置,则可以将position设为fixed,此时topleftbottomright就是相对于可视画面。

想使用 JavaScript 来获取元素的确实位置,必须知道offsetParent为何,offsetParent并非元素的直接父节点,而是在元素的所有父阶层中,第一个可用来作为位置参考的节点。

每个元素都可以获取offsetTopoffsetLeft,分别代表距offsetParent外边框左上角的距离,所以,在简单的排版下,想要知道元素在版面中的确实位置,基本上可以如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">

    <style type="text/css">
        #container {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            width: 100px;
            height: 50px;
            padding: 50px;
            position: absolute;
            top: 50px;
            left: 50px;
        }

        #message {
            position: static;
            top: 20px;
            left: 20px;
        }
    </style>  
</head>
<body>

    <div id="container"><span id="message">这是一段消息<span></div>
    <span id="console"></span>  

<script type="text/javascript">
    function offset(elem) {
        let x = 0;
        let y = 0;
        while(elem) {
            x += elem.offsetLeft;
            y += elem.offsetTop;
            elem = elem.offsetParent;
        }

        return { 
            x, 
            y, 
            toString() {
                return `(${this.x}, ${this.y})`;
            }
        };
    }

    document.onclick = function(evt) {
        let container = document.getElementById('container');
        container.style.left = `${evt.clientX}px`
        container.style.top = `${evt.clientY}px`;
        let message = document.getElementById('message');
        let console = document.getElementById('console');
        console.innerHTML = offset(message);
    };                
</script>       

</body>
</html>

按此观看结果

一个需要元素确实位置的例子,就是像搜寻框中出现的提示,你需要知道搜寻框的位置,将返回的关键字建议显示在搜寻框下方。下面这个例子是个简单示范:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <style type="text/css">
         #container {
             color: #ffffff;
             background-color: #ff0000;
             height: 50px;
             position: absolute;
             top: -100px;
             left:-100px;
         }
    </style>
</head>
<body>

    <div id="container">这是一段消息</div>
    <hr>
    搜寻:<input id="search" type="text">

<script type="text/javascript">
    function offset(elem) {
        let x = 0;
        let y = 0;
        while(elem) {
            x += elem.offsetLeft;
            y += elem.offsetTop;
            elem = elem.offsetParent;
        }

        return { 
            x, 
            y, 
            toString() {
                return `(${this.x}, ${this.y})`;
            }
        };
    }

    let input = document.getElementById('search');
    let search = offset(input);
    let container = document.getElementById('container');
    container.style.left = `${search.x}px`;
    container.style.top = `${search.y + input.offsetHeight}px`;
    container.style.width = `${input.offsetWidth}px`;
</script>

</body>
</html>

按我观看结果

(如果想做搜寻框的自动提示清单,如果能使用 HTML5,透过<datalist>会更方便。)

在更复杂的排版下,获取元素精确位置所要考虑的事情会更复杂,例如,CSS 的overflow设定可创建卷轴,偏移量不会考虑卷轴位置,如果使用了overflow设定,必须再考虑卷轴值。例如:

function offset(elem) {
    let x = 0;
    let y = 0;
    for(let e = elem; e; e = e.offsetParent) {
        x += e.offsetLeft;
        y += e.offsetTop;
    }

    //  修正卷轴局部的量
    for(let e = elem.parentNode; e && e != document.body; e = e.parentNode) {
        if(e.scrollLeft) {
            x -= e.scrollLeft;
        }
        if(e.scrollTop) {
            y -= e.scrollTop;
        }
    }

    return { 
        x, 
        y, 
        toString() {
            return `(${this.x}, ${this.y})`;
        }
    };
}




展开阅读全文