iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0

❒ DOM ( Document Object Model )

DOM 結點樹 維基百科

  • 方便 js 選取

  • 方便 css 渲染到對應標籤

  • 當解析完一個網頁內容 ( 開啟一個網頁 ),會生成一個 DOM 也就是網站的節點樹 ( dom tree ) 如下,Document 為最根目錄的地方,所以要先從根目錄開始選取才能選到裡面的標籤 ( 例如 h1 、a )。
    簡述 : 網頁一打開,會生成出一個 document。
    DOM 結點樹

瞭解 document 重要性 ( 以 HTML 解說 ) :

  • 網頁在解析瀏覽器時,會先產生 Document ,Document 內就會依序解析裡面有些甚麼元素。
  • 可從 JS 內改 HTML 內的文字,例如 document.getElementById().textContent ,就是從 Document 依序往內找到 Text 更改裡面的文字。
    網頁一打開,會生成出一個 document。
    // 網頁一打開,會生成出一個 document。( 圖片出處 - 六角學院 ) ▲

DOM 環境配置

出處 : JavaScript 工程師養成直播班 - 2021 春季班 影音

<body>
	<script src="js檔案位置"></script>
</body>
  • <script src="js檔案位置"></script> 放置到 </body> 結尾前。
  • 為什麼要放 <body> 內而非 <head> → 網頁打開,會從上往下執行,<body> 內的標籤轉化為 DOM 節點後,再依序往下執行 js 檔案才能夠解析到這些 DOM 節點。( 產生之後才能夠讀取 )

document.body

  • Document 介面代表所有在瀏覽器中載入的網頁,也是作為網頁內容 DOM Tree(包含 、 與其它的元素)的進入點。
  • Document.body 主要是「回傳目前文件的 <body> 節點,如元素不存在則回傳 null 」,因此只有 body 能使用。

其他常見選取 DOM 節點的方法,建議使用:

  1. 根據傳入的值,找到 DOM 中 ID 為 'xxx' 的元素 → document.getElementById('xxx');

  2. 針對給定的 Selector ( 選擇器 ) 條件,回傳第一個或所有符合條件的 NodeList ( 節點列表 )。

    document.querySelector('xxx');
    document.querySelectorAll('xxx');
    

❒ querySelector 與 getElementById 選擇單一元素

範例 1. 使用變數縮短程式碼,使用 getElementById

  • getElementById 只可用來選取 ID ( 加井字號 # ) 的語法。
  • CodePen範例
<h1 id="textId" class="textClass"> text </h1>
// 程式碼太長
// document.getElementById('textId').textContent = '123';

// 使用變數縮短程式碼
const element = document.getElementById('textId');
element.textContent = '13579';

範例 2. 使用 querySelector

  • querySelector() 可以用來選取 ID ( 加井字號 # ) 或 CLASS ( 加點 . ) 的語法。
  • CodePen範例
<h1 id="textId" class="textClass">
	<em>text</em>
</h1>
const element = document.querySelector('.textClass em')
element.textContent = '2468';

getElementById()querySelector() 差異 ?

  • 兩者相同點 : 選取 HTML 元素。
  • 相異點
    • getElementById() 選取元素侷限於 ID
    • querySelector() 選取元素包含 HTML 標籤、 ID 元素 、 CLASS 元素 。

❒ querySelectorAll 可重覆選取多個元素

querySelector()querySelectorAll() 差異 ?

querySelector() 只會抓第一筆資料做更新
querySelectorAll() 可選擇多筆資料做更新

範例 1. querySelector() 只會抓第一筆資料做更新

<h1 id="textId" class="textClass">
	<em>text</em>
</h1>
<h1 id="textId" class="textClass">
	<em>text</em>
</h1>
var element = document.querySelector('.textClass em');
element.textContent ='123';

解析

  • 因為querySelector() 只會抓第一筆資料做更新,所以只有第一筆有改到內容。
    開發者工具截圖

範例 2. 不知陣列數量使用 querySelectorAll 選擇多筆資料做更新

const element = document.querySelectorAll(".textClass em");

// element 數量
const elementTotal = element.length;

for (let i = 0; i < elementTotal; i++) {
  //不要把陣列內的數值寫死,所以寫i,讓他去跑小括號內的條件
  element[i].textContent = "text一起做更改";
}

CodePen 範例

  • querySelectorAll 可選擇多筆資料做更新,指定的東西會回傳陣列。
    • 不知陣列數量 : 寫 for loop ( 迴圈 ) 。
      • length 查數量,再帶入迴圈中

範例 3. 知到陣列數量使用 querySelectorAll 選擇多筆資料做更新

<h1 class="textClass">
	<em>text</em>
</h1>
<h1 class="textClass">
	<em>text</em>
</h1>
const element = document.querySelectorAll('.textClass em')
element[0].textContent = 123;
element[1].textContent = 123;

CodePen 範例

  • 知道陣列數量 : 依序在 Array 中帶值進去 。
    • querySelectorAll 指定的東西會回傳陣列。
    • 明確知道 .textClass em 數量,就可以依序帶值進去
      NodeList 為節點列表
      // console.log(element); 回傳陣列 ( NodeList 為節點列表 ) ▲

❒ setAttribute 增加、修改標籤屬性

結構: setAttribute( '要更改的屬性' , '更改的屬性值' )

範例 1. 透過 JS 動態新增 HTML 中 <a> 標籤的連結

<h1 class="textClass">
	<a href="#">title</a>
</h1>
const element = document.querySelector('.textClass a');
element.setAttribute('href','https://google.com');

****CodePen範例

範例 2. 透過 JS 動態增加屬性調整文字顏色,把文字由黑變紅

<div id="textHere">Hello</div>
const element = document.getElementById('textHere');

//動態改變 textHere 屬性
element.setAttribute('ID','textRed');

CodePen 範例

  • 新增一個紅色文字 id 於 css 中,並使用 setAttribute 把此屬性新增於 element 上,Hello 文字由黑變紅。
  • 可由開發者工具看到 HTML 內由 JS 動態新增了 id="strID"
    開發者工具截圖

❒ 從HTML 取出值的辦法 ( 撈出屬性、結構、文字 )

getAttribute → 取出 HTML 中的屬性
innerHTML → 取出 HTML 中的結構
textContent → 取出 HTML 中的文字

範例 1. 撈出 HTML 中 h2 標籤內的 a 連結網址

<h1 class="textTitle">TITLE</h1>
<h2 class="textLink">
    <a href="https://google.com">LINK</a>
</h2>
<div class="textTex">Hello</div>
// 方法 1.
const element = document.querySelector('.textLink a').getAttribute('href');
console.log(element);

// 方法 2
const element = document.querySelector('.textLink a');
console.log(element.getAttribute('href'));

  • 使用 getAttribute('屬性') 方式可以撈出 a 標籤內的連結。

範例 2. 取出 .textLink 的 HTML 結構

<h2 class="textLink">
    <a href="https://google.com">LINK</a>
</h2>
//方式一
const element = document.querySelector('.textLink')
console.log(element.innerHTML);

//會撈出 h2 內的所有結構
//<a href="https://google.com">LINK</a>

// 方式二 :先賦予在變數上
const element = document.querySelector('.textLink')
const content = element.innerHTML;
console.log(content);
  • 使用 innerHTML 取出 h2 標籤內的 a 結構。

範例 3. 撈出 HTML 中 h1 標籤中的文字

<h1 class="textTitle">TITLE</h1>
<h2 class="textLink">
    <a href="https://google.com">LINK</a>
</h2>
<div class="textTex">Hello</div>
const element = document.querySelector('.textTitle').textContent;
console.log(element);
  • textContent : 只會抓節點內的 『 文字 』 資訊。

❒ 表單取值與更改值方式 value

出處 : avaScript 工程師養成直播班 - 2021 春季班 影音

範例 1. 取出 input 表單中 " 你好嗎 ? " 的文字

表單示意圖

<input type="text" class="txt" value="你好嗎?">
const txt =document.querySelector('.txt');

//文字寫在 value 上,直接用 txt 選取 value
console.log(txt.value);  // 你好嗎

範例 2. 更改 input 表單中 " 你好嗎 ? " 的文字

<input type="text" class="txt" value="你好嗎?">
// 修改 list.value 的作用也不是給 select 賦予 value 值,而是更改它所指向的 option
const txt = document.querySelector('.txt');
txt.value = "Hello !";
console.log(txt.value);  // Hello

範例 3. 取出 input 下拉選單表單中的文字

下拉選單示意圖

<select class="list">
    <option value="高雄市">高雄市</option>
    <option value="台北市">台北市</option>
</select>
const list =document.querySelector('.list');
console.log(list.value);

範例 4. 設定 input 下拉選單顯示的 option 文字

下拉式選單的文字釘在台北市上。
下拉選單示意圖

<select class="list">
    <option value="高雄市">高雄市</option>
    <option value="台北市">台北市</option>
</select>
// 修改 list.value 的作用也不是給 select 賦予 value 值,而是更改它所指向的 option,因此 console.log(list.value); 一次只會出現一個值。
const list =document.querySelector('.list');
list.value = "台北市";
console.log(list.value);

為什麼 console.log(list.value); 只讀取到高雄市 ?

  • list DOM 是選取到 select 這個標籤,select 本身是沒有 value 值的,它實際上只是回傳它所選取到的那個 optionvalue,並不是直接指向 option
    修改 list.value 的作用也不是給 select 賦予 value 值,而是更改它所指向的 option,因此 console.log(list.value); 一次只會出現一個值。

    // 可看出 select 本身沒有 value 屬性,但可以回傳它所選取到的那個 option 的 value
    const list =document.querySelector('.list');
    console.log(list.getAttribute('value')); //回傳 null
    

❒ 插入 HTML 標籤的兩種方法

用 JavaScript 操控 HTML 的方法

  1. innerHTML
    1. 方法 : 組完字串後,傳進語法進行渲染。
    2. 優點 : 效能快。
    3. 缺點 : 資安風險,要確保來源沒問題 。
    4. 特性 : 會在指定的區域把原來的內容清空,放入新的值。
  2. createElement('')
    1. 方法 : 以 DOM 節點來處理。
    2. 優點 : 安全性高。
    3. 缺點 : 效能差 。
    4. 特性 : 會保留原本內容,新增的值會依序動態加在原內容後方。

➊ innerHTML

innerHTML 有個特性,會在指定的區域把原來的內容清空,放入新的值。

範例 0. 在 main 標籤中使用 innerHTML

<div class="main">TITLE</div>
const main = document.querySelector('.main');
main.innerHTML = '<h1>TITLE</h1>';

CodePen範例

  • 原本的 TITLE 被清空,以 <h1>TITLE</h1> 取代。
    https://ithelp.ithome.com.tw/upload/images/20221012/20119743UXJ4MHyvwg.png

範例 1. 在 <div id="main"></div> 內塞 H1 標籤

<div id="main"></div>
//方法1
//'<h1>TITLE</h1>' 也可以寫入class
const element = document.getElementById('main');
element.innerHTML = '<h1>TITLE</h1>'

//方法2
const element = document.getElementById('main');
const titleElement = '<h1>TITLE</h1>';
element.innerHTML = titleElement ;

// 試試 element.innerHTML = titleElement + titleElement; 會跑兩次唷!

開發者工具截圖
// 從開發者工具可見,#main 內新增了 h1 標籤 ▲

ES6 之前的寫法

"<h1 class="blue">123</h1>" - - - 錯誤

'<h1 class="blue">123</h1>' - - - 正確

外與內的單引號和雙引號需不同,否則解釋器會因無法分辨開頭結尾而報錯誤。

ES6 樣板字面值寫法 ( 使用反引號 ```` tab 上方的按鍵 )

<h1 class="blue">123</h1>
字串使用反引號 就是 ES6 的樣板字面值寫法,樣板字面值寫法的好處是可以用 ${ } 直接帶入變數,不需使用 + 將每個字串及變數相加。

ES6 樣板字面值運用

內容太龐大,可以使用變數以組字串方式帶入 innerHTML,這邊使用 ul li 為範例。

範例 1. 把 li 內容帶入 ul 中 Codepen範例 ( ES6 之前寫法 )

HTML : <ul class="list"></ul>

const element = document.querySelector('.list');

let name = '卡斯柏'
let webSite = 'https://www.google.com';

//注意內外雙單引號用法& 變數帶入時前後以引號和加號包覆
//先在li外面用引號包起,再把裡面要帶入變數的用不同的引號包起,記得前後要有加號。
element.innerHTML = '<li><a href = "'+webSite+'">'+name+'</a></li>' ;

https://ithelp.ithome.com.tw/upload/images/20221012/20119743jKNALdZ1lh.jpg

範例 1-1. 把 li 內容帶入 ul 中 ( ES6 樣板字面值寫法 )

const element = document.querySelector('.list');
let name = '卡斯柏';
let webSite = 'https://www.google.com';

// 使用樣板字面值寫法 - 不須擔心雙引號與單引號錯誤使用
element.innerHTML =`<li><a href='${webSite}'>${name}</a></li>`;

另外也可賦予字串變數,日後如果要新增很多個 li ,可以於 innerHTML 使用組字串的方式,如下 :

const element = document.querySelector('.list');

let name = '卡斯柏';
let webSite = 'https://www.google.com';
let content = `<li><a href='${webSite}'>${name}</a></li>`;

element.innerHTML = content + content;

➋ innerHTML 搭配 for loop 運用

從 Array 中撈出資料,顯示在網頁上。

範例 1. 列出每個農場農夫的名字。 CODEPEN

//農場陣列資訊
const farms = [
  {
    farmer: '卡斯伯',
    dogs: ['詹姆士', '龐德']
  },
  {
    farmer: '查理',
    dogs: ['皮皮']
  },
];

防止資料被蓋掉

innerHTML 特性 : 新帶入的值會把舊的值蓋過,所以查理會蓋過卡斯伯。迴圈內每把變數 farmersTotal 帶入跑一次迴圈,組好字串帶入又會把先前資料蓋過。

  • 先寫一個 let farmersName = ''; 空值的全域變數,來做累加字串。
  • let farmersName = ''; 本來是空字串,在迴圈內 farmersName += farmersAllName; 依序加入變數 farmersAllTotal 的內容 ( <li>${farms[i].farmer}</li>; ),就會有第一筆、第二筆依序跑出陣列中所有資料。
// 錯誤寫法
const element = document.querySelector('.list');
const farmsLength=farms.length;

for( let i=0; i<farmsLength; i++){
    let farmersAllName = `<li>${farms[i].farmer}</li>`;

	 // innerHTML 有新值會把舊值蓋掉的特性,所以每跑迴圈一次就會蓋掉之前的資料。網頁都只會顯示查理
    element.innerHTML = farmersAllName;
}
// 正確:for 迴圈寫法
const element = document.querySelector('.list');
const farmsLength=farms.length;
let farmersName='';

for(let i=0; i<farmsLength; i++){
  let farmersAllName = `<li>${farms[i].farmer}</li>`;
  farmersName+=farmersAllName;
}
element.innerHTML=farmersName;
// forEach 寫法
const element = document.querySelector('.list');
let farmersName = '';
farms.forEach((i) => {
  farmersName += `<li>${i.farmer}</li>`;
})
list.innerHTML = farmersName;

❒ createElement 搭配 appendChild 寫法

createElement('') 會保留原本內容,新增的值會依序動態加在原內容後方。 CODEPEN
appendChild() 在節點增加最後一個子節點。

  • 下方範例可見,網頁中的 TITLE 並沒有被清空,新增的值 123 依序的排在 TITLE 後方。

拆解步驟

  1. HTML 內新增一個節點。 const str = document.createElement('em');
  2. 新增節點內容 ( 動態新增文字內容 ) 。 str.textContent = '123';
  3. 因為前方未設定節點位置,所以這邊把節點掛在 H1 ( .title ) 內。 document.querySelector('.title').appendChild(str);

使用 appendChild() 會動態掛在 HTML 裡面元素 <em>TITLE</em> 的最後面位置。

<h1 class="title">
    <em>TITLE</em>
</h1>

//新增一個節點 (新增 p 元素)
const str = document.createElement('p');

//動態增加內容 → 變數str 放置123文字內容。
str.textContent = '123';

//新增子節點 → 在 .title 的位置掛上子節點,子節點內放入上方新增的 p段落與文字內容 123。
document.querySelector('.title').appendChild(str);

範例 1. 在 HTML 內動態新增文字 567 ,並設定紅色。 codepen

<head>
	<style>
        .textRed {
            color: red;
        }
    </style>
</head>
<body>
	<h1 class="title">
	    <em>TITLE</em>
	</h1>
</body>
//-------方法一
//取出容器
var space = document.querySelector('.title');

//新增p元素
var element = document.createElement('p');

//新增文字
element.textContent = '567';

//新增屬性
element.setAttribute('class','textRed');

//把 element(元素、文字、屬性都在裡面) 丟到容器內
space.appendChild(element);
//-------方法二

//新增節點標籤
let element = document.createElement('p');

//新增節點內容 & 新增 class
element.textContent = '567';
element.setAttribute('class','textRed');

//設定節點位置 (在.title後方掛上子節點,加入上方新增的標籤與內容 )
document.querySelector('.title').appendChild(element);

❒ createElement 與 for loop 運用

三步驟

  1. 新增節點標籤
  2. 新增節點內容
  3. 設定節點位置

範例1. 列出每個農場農夫的名字。

//農場陣列資訊
var farms =[
    {
        farmer:'卡斯伯',
        dogs:['詹姆士','龐德']
    },
    {
        farmer:'查理',
        dogs:['皮皮']
    },
];

CodePen 範例

❒ XSS 跨網站指令碼注意事項(Cross-site scripting)

XSS ( Cross-site scripting ) 跨網站指令攻擊 : innerHTML 只是其中一個可以使用 XSS 的方法。刻意在 HTML 標籤或 JavaScript 埋一些惡意程式碼,做攻擊的動作。

不是讓使用者自己做操控 →

例如 : 留言板等表單輸入的東西,A 用戶留完 B 用戶看的到,但 A 用戶在裡面塞 <script src='病毒.js'></script> ,雖然網頁表面看不到,但在開發者工具查看會發現已被植入病毒。

通常這些讓用戶自行輸入的留言板,會做一些排錯,過濾掉可疑的語法,都確認沒有錯了才會送入資料庫。
https://ithelp.ithome.com.tw/upload/images/20221012/20119743RcqQzETpAW.png

可信任 → 政府提供的 open data 或自己建立的資料庫,例如 : 鄉鎮陣列資料庫 .. 等

資料來源


上一篇
Day26 | JS 箭頭函式
下一篇
Day28 | JS Event 事件
系列文
那些 Notion 中的前端筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言