下方是 W3CShool 所提供的圖,他簡單跟我們頗析了網頁的結構,就跟我們之前的課程所提過的一樣,Web 本質上就是一個巨大的 Object,因此操作 DOM 的意思,其實就是用程式運行的方式,動態控制著這個網頁所發生的一切。
<script>
document.title = '我是新的標籤';
</script>
在上方的案例中,我們很輕鬆的改變了網站上的 title,doucment 作為我們文件的主體,是很常拿來進行操作的物件,大家不妨用 console.log(document),來查看這個物件裡面有什麼可以改吧!
<body>
<button onclick="myFunction()">點擊我</button>
<div id="myDiv">This is my DIV element.</div>
</body>
<script>
// 取得物件實體
var myDiv = document.getElementById('myDiv');
// 可以看到這個實體內的「文字內容」
console.log(myDiv.textContent);
// 建立一個用於互動的函示
function myFunction() {
if (myDiv.style.display === 'none') {
myDiv.style.display = 'block';
} else {
myDiv.style.display = 'none';
}
}
// 改個寫法並用三元運算式
// const myFunction = function(){
// myDiv.style.display === 'none'
// ? (myDiv.style.display = 'block')
// : (myDiv.style.display = 'none');
// }
</script>
從上面的練習中我們不難看出,實際上 JavaScript 在操作 DOM 時是相對直覺的,但網頁設計終究不是一個單體,這次就讓我們搭配起 CSS 來呈現畫面吧!
<style>
.height-line {
border-bottom: 1px solid #ffccee;
}
</style>
<body>
<button onclick="addColor()">點擊我修改顏色</button>
<button onclick="addClass()">點擊我新增樣式類別</button>
<div id="myDiv">This is my DIV element.</div>
<span id="mySpan">This is my SPAN element.</span>
</body>
<script>
// 建立實體物件
var myDiv = document.getElementById('myDiv');
var mySpan = document.getElementById('mySpan');
// 新增顏色功能
var addColor = function () {
myDiv.style.color = 'red';
};
// 新增樣式功能
var addClass = function () {
myDiv.className = 'height-line';
mySpan.className = 'height-line';
};
// 帶入變數的顏色修改
// var addColor = function (color) {
// myDiv.style.color = color;
// };
</script>
上方的範例也可以帶入多個變數進行複雜的操作,例如像下面那樣給複數的參數,先動態建立目標實體後,在進行額外的操作。
小叮嚀: 物件的 style 屬性可以操作 CSS 的大部分特性
var changeTargetColor = function (target, color) {
var targetElement = document.getElementById(target);
targetElement.style.color = color;
};
changeTargetColor('mySpan','blue')
上面的範例基本上均為練習操作,而現在我們已經擁有足夠的練習量,是時候該來製作一個常用 UI 出來了!
在實作之前,我們先思考一下我們目標是什麼,這是寫程式開發非常重要的一環,因此等等在實作的過程中,我們也會將 HTML、CSS、JavaScript 的部分先進行分離,首先讓我們開始思考,我們要結構要長得的怎麼樣吧!
<body>
<article>
<ol>
<li>標籤一</li>
<li>標籤二</li>
<li>標籤三</li>
</ol>
<section>標籤項目一</section>
<section>標籤項目二</section>
<section>標籤項目三</section>
</article>
</body>
符合大家的預期嗎?這邊的設計是思考說作為一個可以切換的功能,應該要有一個包覆在最外面的主層,而子項目會有兩個分支,一個是標籤的按鈕列,另外一個是內容的呈現頁,當然這裡也可以做一層額外的包覆,但這類型本身就是見仁見智的設計方式.
接下來讓我們思考一下樣式的問題吧!儘管這麼說,但可惜的是我們是以 JavaScript 為主的課程,不過下方的樣式還是能大概說明一下的,我們首先對 Body 做了定義,但這只是為了讓之後的內容置中好查看而已,之後就是對 tab-box 本身進行設計,還另外設計了當 tab 被點擊時會產生的效果,另外各位同學也要記得,要把這些樣式加到我們的 HTML 上才有用喔!
<style>
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.tab-box {
display: flex;
flex-direction: column;
width: 400px;
}
.tab-box section {
padding: 1rem;
min-height: 5rem;
background-color: bisque;
}
.tab-box__header {
display: flex;
padding-left: 0;
list-style: none;
}
.tab-box__header > li {
margin-right: 10px;
}
.tab-box__header > li.active {
color: blueviolet;
}
</style>
<body>
<article class="tab-box">
<ol class="tab-box__header">
...
終於來到最後的重頭戲了,為了方便學習我們會設計兩個版本出來,一個是以上面作為基礎延伸的版本,另外一個是藉由 「程式」 特性實作的版本,那就先嚷我們從簡單的開始吧!
<style>
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.tab-box {
display: flex;
flex-direction: column;
width: 400px;
}
.tab-box section {
padding: 1rem;
min-height: 5rem;
background-color: bisque;
}
.tab-box__header {
display: flex;
padding-left: 0;
list-style: none;
}
.tab-box__header > li {
margin-right: 10px;
cursor: pointer;
}
.tab-box__header > li.active {
color: blueviolet;
}
</style>
</head>
<body>
<article class="tab-box" id="tab-box">
<ol class="tab-box__header">
<li onclick="triggerSection(event,'targetOne')" id="defaultOpen">
標籤一
</li>
<li onclick="triggerSection(event,'targetTwo')">標籤二</li>
<li onclick="triggerSection(event,'targetThree')">標籤三</li>
</ol>
<section id="targetOne">標籤項目一</section>
<section id="targetTwo">標籤項目二</section>
<section id="targetThree">標籤項目三</section>
</article>
</body>
<script>
// 取得全部需要使用的 UI
var tabBox = document.getElementById('tab-box');
var tabBoxTab = document.querySelectorAll('li');
var tabBoxSection = document.querySelectorAll('section');
var triggerSection = function (event, target) {
// 消除全部的 Section 顯示
tabBoxSection.forEach(function (element) {
element.style.display = 'none';
});
// 取得目標實體
var targetElement = document.getElementById(target);
// 顯示目標
targetElement.style.display = 'block';
// 清除 tab 上的樣式並且將目前點擊的目標加上 active
tabBoxTab.forEach(function (element) {
element.classList = '';
});
event.currentTarget.className += ' active';
};
// 預設出發此名稱的點擊事件
document.getElementById('defaultOpen').click();
</script>
相對直覺的程式對吧!除了使用 forEach 來處理 DOM 元素以外,基本上都是我們老生常談的東西了,當然當中也有像是點擊事件傳入,以及預設點擊操作這種行為,但其實都還算好理解的範圍,但現今這樣做其實是有弊端的,各位可以看到我們與 DOM 本身的牽扯性極強,包括需要給予很多 ID 這點,對於一個可重複利用與需要額外擴充的 UI 都非常不良,因此下面就讓我們用程式的思維,來解決這過於土法煉鋼的問題吧!
<style>
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.tab-box {
display: flex;
flex-direction: column;
width: 400px;
}
.tab-box section {
padding: 1rem;
min-height: 5rem;
background-color: bisque;
}
.tab-box__header {
display: flex;
padding-left: 0;
list-style: none;
}
.tab-box__header > li {
margin-right: 10px;
cursor: pointer;
}
.tab-box__header > li.active {
color: blueviolet;
}
</style>
<body>
<article class="tab-box" id="tab-box">
<ol class="tab-box__header">
<li>標籤一</li>
<li>標籤二</li>
<li>標籤三</li>
</ol>
<section>標籤項目一</section>
<section>標籤項目二</section>
<section>標籤項目三</section>
</article>
</body>
<script>
var tabBox = document.getElementById('tab-box');
// 在已經搜尋出的 DOM 物件內在進行搜尋
var tabBoxTab = tabBox.querySelectorAll('li');
var tabBoxSection = tabBox.querySelectorAll('section');
var triggerTab = function (tabNumber) {
tabBoxSection.forEach(function (element, index) {
element.style.display = 'none';
// 使用 forEach 自帶的 index,同時對 tabBoxTab 進行同樣的處理
tabBoxTab[index].className = '';
});
tabBoxSection[tabNumber].style.display = 'block';
tabBoxTab[tabNumber].className = 'active';
};
tabBoxTab.forEach(function (element, index) {
element.onclick = function () {
triggerTab(index);
};
});
triggerTab(0);
</script>
所謂的變數以及參數,其實有著許多我們可以額外操作的細節空間,在這個範例中,我們藉由陣列的特性將原先兩次的 forEach 合併處理,並且同時處理了原先綁定在 UI 上的操作,讓程式的延展性提供,雖然以目前的結果而言都是好處居多,但其實大家應該不難看出,當我們與 HTML 進行分離的同時,整個設計其實也開始愈發抽象了。
<body>
<p id="downDate"></p>
</body>
<script>
// 定義倒計時目標時間
var countDownDate = new Date('July 15, 2022 16:10:25').getTime();
// 新增每秒更新時間的函示
var x = setInterval(function () {
// 取得今日的日期
var now = new Date().getTime();
// 找出現在和倒計時日期之間的時間
var distance = countDownDate - now;
//天、小時、分鐘和秒的時間計算
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
var hours = Math.floor(
(distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
);
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
// 在 id="downDate" 的元素中顯示結果
document.getElementById('downDate').innerHTML =
days + 'DAY ' + hours + 'h ' + minutes + 'm ' + seconds + 's ';
// 如果倒計時結束的話,就顯示一些預設文字
if (distance < 0) {
clearInterval(x);
document.getElementById('downDate').innerHTML = '倒數已結束';
}
// console.log(parseInt(Math.random() * 10));
}, 1000);
</script>