存取樣式資訊

September 26, 2022

在 HTML 標籤上,可以設定 style 屬性,藉以改變元素的樣式。例如:

<div style="color: #ffffff; background-color: #ff0000; width: 500px; height: 200px; padding-left: 250px; padding-top: 150px;">這是一段訊息</div>

可以直接使用 JavaScript 設定 style 特性上的各個特性來達到相同效果。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>
    <div id="message">這是一段訊息</div>
    
<script type="text/javascript">
    let message = document.getElementById('message');
    message.style.color = '#ffffff';
    message.style.backgroundColor = '#ff0000';
    message.style.width = '500px';
    message.style.height = '200px';
    message.style.paddingLeft = '250px';
    message.style.paddingTop = '150px';
</script>  

</body>
</html>

按此觀看執行結果

屬性/特性/樣式表

style 特性參考的是 CSSProperties 物件,注意特性名稱的大小寫,通常是沒有破折線、採駝峰式命名。一般不會或不建議使用標籤的 style 屬性來設定樣式,而是定義在樣式表中。例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <style type="text/css">
        #message {
            color: #ffffff; 
            background-color: #ff0000; 
            width: 500px; 
            height: 200px; 
            padding-left: 250px; 
            padding-top: 150px;
        }
    </style>
</head>
<body>

    <div id="message">這是一段訊息</div>
    
</body>
</html>

按此觀看執行結果

然而在這種情況下,無法使用 style 特性來取得樣式表中定義的各個樣式資訊,因為元素的 style 特性是對應於標籤的 style 屬性,標籤若沒有定義 style 屬性,或程式中沒有設定 style 的特性,就無法使用 style 特性來取得樣式資訊。

標籤的 style 屬性或透過元素的 style 特性會覆蓋樣式表的設定,由於一般並不建議直接於標籤上設定 style 屬性,因此透過程式時,通常建議將 style 特性用於設定樣式,而不是取得樣式。

如果標籤沒有設定 style 屬性,而想要取得元素上套用的樣式資訊,則必須取得元素的計算樣式(Computed style),計算樣式是指所有樣式規則(包括樣式表、style 屬性)已套用至元素後的樣式結果。

若是在標準瀏覽器中,可以使用 windowgetComputedStyle 函式(與 document.defaultView.getComputedStyle 是相同函式),取得的樣式物件是唯讀的 CSS2Properties 物件,可以直接如同使用 style 特性,直接於上指定特性來取得樣式資訊,或是使用 getPropertyValue 方法指定 CSS 屬性。

getComputedStyle 的第二個引數可指定虛擬類別(pseudo-class),如果不設定的話,也必須指定 null,例如:

window.getComputedStyle(elem, null)[prop];

來使用程式取得上一個範例網頁的 CSS 樣式設定:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <style type="text/css">
        #message {
            color: #ffffff; 
            background-color: #ff0000; 
            width: 500px; 
            height: 200px; 
            padding-left: 250px; 
            padding-top: 150px;
        }
    </style>
</head>
<body>

    <div id="message">這是一段訊息</div>
    <span id="console"></span>
    
<script type="text/javascript">
    function style(elem, prop) {
        return window.getComputedStyle(elem, null)[prop];
    }

    let message = document.getElementById('message');
    let color = style(message, 'backgroundColor');
    document.getElementById('console').innerHTML = color;
</script>  
</body>
</html>

按此觀看執行結果

元素寬高/大小

可以透過樣式的 widthheight 來設定元素的寬高,但要注意所謂的寬高定義為何,一個元素的寬高如何定義,要看盒模式(Box model),也就是將元素當作一個盒子來看待,每個元素都會有四個部份可以設定。

存取樣式資訊

內容(content)就是放置元素真正有內容的部份,像是一段文字、一個圖片或者是其它子元素。每個元素都可以有邊框(border)。內距(padding)是指內容區與邊框的距離。邊距(margin)則是指與另一元素的邊框距離。內容可以設定寬、高,內距的上、下、左、右可以分別設定距離,邊框可以設定寬度,而邊距的上、下、左、右可以分別設採定距離。

當指定元素的 widthheight 時,在標準 CSS 盒模型,指定的 widthheight 僅指定內容區大小。

存取樣式資訊

在遵守標準的瀏覽器中,就是採取這種盒模型。例如下面這個範例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <style type="text/css">
        img {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            width: 100px;
            height: 82px;
            padding: 50px;
            margin: 10px;
        }
        
        div {
            color: #ffffff;
            background-color: #ff0000;
            border-width: 10px;
            border-color: black;
            border-style: solid;
            text-align: center;
            width: 100px;
            height: 82px;
            padding: 50px;
            margin: 10px;
        }            
    </style>
</head>
<body>

    <img src="https://openhome.cc/Gossip/images/caterpillar_small.jpg">
    <div>內容</div>
    
</body>
</html>

存取樣式資訊

範例中的圖就是內容區,寬、高為 100x82,加上內距 50px 與邊框 10px,元素視覺上看得到的部份為 (100+502+102)x(82+502+102),也就是 220x202。下方了一個內容區寬、高,內距與邊框相同的 <div> 以資對照。

存取樣式資訊

每個元素都會有 offsetWidthoffsetHeight 特性,這兩個特性是唯讀的,可分別取得元素的寬與高,在標準 CSS 盒模型下,所取得的是邊框、內距與內容區的加總大小:

存取樣式資訊

元素位置

樣式的 position 屬性可以設定不同的值,代表元素的定位方式,如果沒有設定,大多數元素預設是 static,元素依序、流動式地繪製版面上,其他常用的設定值有 relativeabsolutefixed

元素的 top 特性表示元素上緣,與參考元素上緣的垂直距離,正值為往下,left 表示元素左緣與參考元素左緣的水平距離,正值為往右。bottom 表示下緣與參考元素的下緣垂直距離,正值為往上,right 表示元素右緣與參考元素的右緣水平距離,正值為往左;這些特性都包含含邊距,而設定後的效果,必須視 position 是哪個而定,例如,設為 static 時,topleftrightbottom 的設置沒有作用。

relative 是指元素相對於static應有的位置進行偏移,也就是依序、流動式地繪製版面之後,依 topleftbottomright 設定偏移。例如,若元素左上位置,在本來流動式地繪製後 100x50,若設定元素的 positionrelative,而 topleft 分別為 20、30 的話,元素最後的左上角位置會是 (100+20)x(50+30)=120x80 的位置。

position 屬性設為 absolute 表示絕對定位,不過這個絕對,並不是以 body 左上角為絕對參考點,而是以最接近的非 static 父元素為絕對定位參考元素,也就是說,若有個 arelative 定位,其中有個 spanabsolute 定位,那麼 span 設定的 topleft,會是以 a 元素的上緣與左緣來進行絕對定位。

在絕對定位時,元素可以堆疊,此時可利用 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>

按此觀看結果

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 標籤會更方便。

在更複雜的排版下,取得元素精確位置所要考慮的事情會更複雜,例如,樣式表的 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})`;
        }
    };
}

分享到 LinkedIn 分享到 Facebook 分享到 Twitter