存取樣式資訊
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 屬性)已套用至元素後的樣式結果。
若是在標準瀏覽器中,可以使用 window 的 getComputedStyle 函式(與 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>
元素寬高/大小
可以透過樣式的 width 與 height 來設定元素的寬高,但要注意所謂的寬高定義為何,一個元素的寬高如何定義,要看盒模式(Box model),也就是將元素當作一個盒子來看待,每個元素都會有四個部份可以設定。

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

在遵守標準的瀏覽器中,就是採取這種盒模型。例如下面這個範例:
<!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> 以資對照。

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

元素位置
樣式的 position 屬性可以設定不同的值,代表元素的定位方式,如果沒有設定,大多數元素預設是 static,元素依序、流動式地繪製版面上,其他常用的設定值有 relative、absolute 與 fixed。
元素的 top 特性表示元素上緣,與參考元素上緣的垂直距離,正值為往下,left 表示元素左緣與參考元素左緣的水平距離,正值為往右。bottom 表示下緣與參考元素的下緣垂直距離,正值為往上,right 表示元素右緣與參考元素的右緣水平距離,正值為往左;這些特性都包含含邊距,而設定後的效果,必須視 position 是哪個而定,例如,設為 static 時,top、left、right、bottom 的設置沒有作用。
relative 是指元素相對於static應有的位置進行偏移,也就是依序、流動式地繪製版面之後,依 top、left 或 bottom、right 設定偏移。例如,若元素左上位置,在本來流動式地繪製後 100x50,若設定元素的 position 為 relative,而 top、left 分別為 20、30 的話,元素最後的左上角位置會是 (100+20)x(50+30)=120x80 的位置。
position 屬性設為 absolute 表示絕對定位,不過這個絕對,並不是以 body 左上角為絕對參考點,而是以最接近的非 static 父元素為絕對定位參考元素,也就是說,若有個 a 是 relative 定位,其中有個 span 是 absolute 定位,那麼 span 設定的 top、left,會是以 a 元素的上緣與左緣來進行絕對定位。
在絕對定位時,元素可以堆疊,此時可利用 z-index 屬性來設定堆疊順序,數值越大,表示在越上層,z-index 預設是頁面中標籤定義的順序,越後面定義的標籤 z-index 越大。
下面這個範例運用了絕對定位、top、left 與 z-index 屬性,可以使用滑鼠點選改變 id 為 message1 的 <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,此時 top、left 與 bottom、right 就是相對於可視畫面。
想使用 JavaScript 來取得元素的確實位置,必須知道 offsetParent 為何,offsetParent 並非元素的直接父節點,而是在元素的所有父階層中,第一個可用來作為位置參考的節點。
每個元素都可以取得 offsetTop、offsetLeft,分別代表距 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})`;
}
};
}


