今天要聊的是使用JavaScript操作樣式表,通常我們在寫網頁時,可以用css來操作的效果,也可以透過JS來達到,以及一些進階事件的處理,我們開始吧!
操作行內樣式
以下範例是用JavaScript做到css裡面hover的效果,
請看程式碼:
<body>
<!--操作範例-->
<div id="elem">滑鼠移入時改變顏色</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let elem = document.getElementById('elem');
//滑鼠移入時改變背景顏色(藍)
elem.addEventListener('mouseover', function () {
this.style.backgroundColor = 'blue';
})
//滑鼠移出時恢復背景顏色
elem.addEventListener('mouseout', function () {
this.style.backgroundColor = '';
})
})
</script>
</body>
上述範例是透過事件監聽來操作div
行內的樣式表,
語法:
elem.style.prop = value
elem:元素物件
style:樣式屬性
value:設定值
要注意的是,如果是用JS來操作,樣式屬性名稱和css裡面的是不同的,例如:
css:background-color / JS:backgroundColor
css:border-top / JS:borderTop
我們可以發現,在JavaScript操作時,需要除 - 連字符好,且第一個英文單字以後的英文單字,開頭第一個字必須為大寫。
套用外部樣式表 className
上述方法,操作行內樣式雖說可以直接在div
中設定樣式,但程式碼看起來會叫雜亂,要修改時也不方便。所以通常我們會將css獨立寫成一個.css
檔案,再載入html裡面。
這邊要介紹使用className屬性來存取.css
檔案中的樣式。
語法:
elem.className = clazz
elem:元素物件
clazz:樣式類別
接著我們來改寫上述操作行內樣式的例子:
css檔案(all.css):
.highlight{
background-color: yellow;
}
html檔案:
<!--載入all.css-->
<link rel="stylesheet" href="./all.css">
<title>My title</title>
</head>
<body>
<!--操作範例-->
<div id="elem">滑鼠移入時改變顏色</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let elem = document.getElementById('elem');
//滑鼠移入時改變背景顏色(藍)
elem.addEventListener('mouseover', function () {
this.className = 'highlight';
})
//滑鼠移出時恢復背景顏色
elem.addEventListener('mouseout', function () {
this.className = '';
})
})
</script>
p.s如果要載入多個樣式類別可以寫成這樣 elem.className = 'clazz','clazz'。
試看看:
在all.css檔案中,新增以下程式碼:.textcolor{color:red;}
接著更改,html檔案中的這行程式碼:this.className = 'highlight';
改為下列程式碼:this.className = 'hightlight textcolor';
我們可以發現,當滑鼠移入時,不僅背景顏色改變了,連字體顏色都改變了。
更簡單的操作樣式類別 classList
我們可以透過classList屬性,更直覺的操作class屬性值。
請看以下範例(改寫上述滑鼠滑入,為點擊):
<body>
<!--操作範例-->
<div id="elem" class="textcolor">滑鼠點擊後改變顏色</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let elem = document.getElementById('elem');
//滑鼠移入時改變背景顏色(藍)
elem.addEventListener('click', function () {
this.classList.toggle('highlight');
})
})
</script>
</body>
上述範例中所使用的toggle是對於樣式類別on/off的切換。
以下附上w3c提供的classList可以用的成員,可以參考參考。
https://www.w3schools.com/jsref/prop_element_classlist.asp
我們在Day11中有提到事件監聽/處理,這邊將針對先前的內容作進階的介紹。
取得事件相關資訊 - 事件物件
事件監聽器/事件處理程序中可以接收一個稱為事件物件的物件,從該物件可以取得事件觸發時的各種資訊,請看例子:
<body>
<!--操作範例-->
<input type="button" value="點擊" id="btn">
<script>
document.addEventListener('DOMContentLoaded', function () {
let elem = document.getElementById('btn');
elem.addEventListener('click', function (e) {
console.log('觸發端:' + elem.nodeName + '/' + elem.id);
console.log('種類:' + e.type)
})
})
//console:
//觸發端:INPUT/btn
//種類:click
</script>
</body>
從上述例子,我們從elem取得事件物件e
,並透過他來接收事件觸發時的相關資訊(console的結果)。
下面附上事件物件可以用的成員列表:
HTML DOM Event Properties and Methods
https://www.w3schools.com/jsref/dom_obj_event.asp
下面我們看一些範例:
顯示某區域內滑鼠指標的所在位置
this關鍵字
我們很常在程式碼內看到this來代表程式觸發端,以下我們比較一下this的用法:
<script>
document.addEventListener('DOMContentLoaded', function () {
let data = {
title: '神鵰俠侶',
price: '460',
show: function () {
console.log(this.title + '/' + this.price + '元');
}
};
document.getElementById('btn').addEventListener('click', data.show);
})
</script>
上述程式碼我們預期在console應該要輸出神鵰俠侶/460元
,但實際上卻是/undefined元
!?
這是因為data.show
沒有參考到data.title和data.price,而是參考到document.getElementById('btn')
,所以輸出是undefined。
而為了避免這樣的問題,我們必須用Function物件內的bind()
方法。
bind()方法的語法:
func.bind(that [,arg1 [,arg2 [...]]])
func:函式物件
that:函式中表示this關鍵字的東西
arg1,arg2...:傳入函式的參數
因此我們可以改寫程式碼:document.getElementById('btn').addEventListener('click', data.show)
為document.getElementById('btn').addEventListener('click', data.show(data))
就可以得到“神鵰俠侶/460元”
w3c的this參考資料:
https://www.w3schools.com/js/js_this.asp
使用箭頭函式固定this
再看一個this的例子:
<body>
<input type="button" id="btn" value="show">
<script>
document.addEventListener('DOMContentLoaded', function () {
let Counter = function (elem) {
this.count = 0;
this.elem = elem;
elem.addEventListener('click', function () {
this.count++;
this.show();
})
}
Counter.prototype.show = function () {
console.log(this.elem.id + '被點擊' + this.count + '次')
}
let c = new Counter(document.getElementById('btn'))
})
//結果:Uncaught TypeError: this.show is not a function
</script>
</body>
在上述例子中,我們可以用之前提到的bind()
方法來解決,也可以用箭頭函式來修改:
將這段程式碼
elem.addEventListener('click', function () {
this.count++;
this.show();
})
修改為
elem.addEventListener('click', () => {
this.count++;
this.show();
})
//結果:btn被點擊1次...2次
便可正常執行,原因是因為在箭頭函式中,this由本身宣告的地方決定,也就是說,它會指向函式所指示的this本身,因此可以輸出結果。
p.s.:箭頭函式是ES6開始才有的,這邊附上MDN的參考資料:
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions
以及更多箭頭函式的實力(出自六角-卡斯伯大神):
https://wcc723.github.io/javascript/2017/12/21/javascript-es6-arrow-function/
希望透過今天的例子,可以再加強一些實務上用得到的方法。