Nội dung

DOM và sự kiện

DOM là gì?

DOM là viết tắt của Document Object Model (Mô hình Đối tượng Tài liệu) dùng để truy xuất các tài liệu dạng HTML và XML. DOM đại diện cho một tài liệu như là một cây cấu trúc dữ liệu. Còn node thì đại diện cho một phần tử trong DOM.

Ví dụ đoạn code HTML dưới đây sẽ mô tả cách trình duyệt hoạt động.

1
2
3
4
5
<div>
  <h1>Bài đồng dao</h1>
  Năm mới năm me
  <!-- TODO: Hoàn thành bài đồng dao -->
</div>

Khi đọc đoạn code này, trình duyệt sẽ xây dựng cấu trúc dạng cây (DOM tree) bao gồm các “DOM node” để giúp quản lí mọi thứ, tương tự như việc bạn xây dựng một cây gia phả để giữ thông tin về mọi người trong dòng họ vậy.

Cấu trúc cây của đoạn HTML trên sẽ giống như sau:

/javascript-dom-event/dom-tree.png
Dom tree

Mỗi phần tử trên DOM tree là một node. Mỗi text là một node. Ngay cả comment cũng là node! Một node đơn giản chỉ là một “mảnh” trên trang web. Và cũng tương tự như trong một cây gia phả, mỗi node có thể có các node con (nghĩa là một mảnh có thể chứa các mảnh khác).

Nếu muốn thay đổi giá trị các thẻ HTML, bạn không cần vào thẳng file HTML thay đổi lại mà chỉ cần thao tác trên DOM. Các phương thức DOM cho phép chúng ta truy cập đến cây cấu trúc và thay đổi cấu trúc, dữ liệu, style, nội dung của document.

Một số lưu ý

  • DOM không phải của Javascript. Để thao tác với HTML DOM chúng ta có thể thực hiện với nhiều ngôn ngữ như PHP, Python, Javascript… Nhưng đối với môi trường trình duyệt thì dùng Javascript.
  • Ở mỗi môi trường sẽ cung cấp các phương thức DOM khác nhau.Ví dụ trình duyệt sẽ cung cấp cho chúng ta các WEB APIs, còn NodeJs thì có thể dùng jsdom
Mẹo
 Bạn có thể Bookmark lại trang này để có gì sau này tham khảo khi đang viết markdown mà quên cú pháp

Thao tác với DOM

Chọn một Element

Element hay HTML Element đơn giản chỉ là những thẻ HTML.

Tìm 1 Element bằng id

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Chọn 1 element từ cây DOM
const title = document.getElementById('title')
console.log(title)

// Lấy một số thuộc tính từ element
console.log(title.id)
console.log(title.className)

// Thay đổi style
title.style.background = '#333'
title.style.color = '#fff'
title.style.padding = '5px'

// Thay đổi nội dung
title.textContent = 'Hello mọi người'
title.innerText = 'Tôi là đời Dev '
title.innerHTML = '<span style="color: blue"></span>'

Tìm một Element bằng selector

Selector là cú pháp CSS giúp tìm kiếm những phần tử trong cây DOM

1
2
3
4
5
6
7
8
console.log(document.querySelector('#task-title'))
console.log(document.querySelector('.card'))

console.log(document.querySelector('h5'))
document.querySelector('li').style.color = 'red'
document.querySelector('ul li').style.color = 'blue'

document.querySelector('li:last-child').style.color = 'red'

Chọn nhiều element

Khi chọn nhiều element bạn sẽ gặp 2 trường hợp là câu lệnh return về HTML Colection hoặc Node List

HTML Collection

Phương thức getElementsByTagName, getElementsByClassName sẽ return về một HTML Collection object

HTML Collection là một object (khá giống array nhưng không phải là array) chứa tập hợp các element. Object này đánh dấu các element thông qua index bắt đầu từ 0.

/javascript-dom-event/htmlcollection.png
HTML Collection

Ví dụ DOM element trong HTML Collection

1
2
3
4
const myCollection = document.getElementsByTagName('p')
for (let i = 0; i < myCollection.length; i++) {
  myCollection[i].style.color = 'red'
}

Node List

Phương thức querySelectorAll() sẽ return về một Node List object

Trong khi HTML Collection chỉ chứa danh sách các thẻ HTML thì NodeList có thể chứa các thẻ html, text, hoặc comment. Node List object sẽ có những phương thức mà HTML Collection object không có.

/javascript-dom-event/nodelist.png
Node List

Sơ lược về các phương thức DOM hay dùng

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const list = document.querySelector('ul')
let val

// get các node con bằng childNodes
// Phương thức này trả về một NodeList
val = list.childNodes

// get các element con bằng children
// Phương thức này trả về một HTML Collection
val = list.children

// get node con đầu tiên
val = list.firstChild

// get element con đầu tiên
val = list.firstElementChild

// get node con cuối cùng
val = list.lastChild

// get element con cuối cùng
val = list.lastElementChild

// get node cha
val = list.parentNode

// get element cha
val = list.parenElement

// get node phía dưới
val = list.nextSibling

// get element phía dưới
val = list.nextElementSibling

// get node phía trên
val = list.previsousSibling

// get element phía trên
val = list.previousElementSibling

Tạo Element

Để tạo một element bất kì ta dùng document.createElement(tên element)

Ví dụ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Tạo element là thẻ li
const li = document.createElement('li')

// Thêm class
li.className = 'collection-item'

// Thêm id
li.id = 'new-item'

// Thêm thuộc tính title
li.setAttribute('title', 'New Item')

// Tạo một text bên trong thẻ li
li.appendChild(document.createTextNode('Hello World'))

// Tạo một thẻ a
const link = document.createElement('a')

// Thêm nhiều class
link.className = 'delete-item secondary-content'

// Gán nội dung HTML bên trong thẻ a là một đoạn HTML mới
link.innerHTML = '<i class="fa fa-remove"></i>'

// Thêm thẻ a vào dưới thẻ li
li.appendChild(link)

// Thêm thẻ li vào dưới element ul
document.querySelector('ul.collection').appendChild(li)

console.log(li)

Xóa và thay thế element

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Thay thế element

// Tạo element
const newHeading = document.createElement('h2')
// Thêm id
newHeading.id = 'task-title'
// Chèn một text vào
newHeading.appendChild(document.createTextNode('Task List'))

// Get element
const oldHeading = document.getElementById('task-title')
// Get element cha
const cardAction = document.querySelector('.card-action')

// Thay thế oldHeading thành newHeading
cardAction.replaceChild(newHeading, oldHeading)

// Xóa Element
const lis = document.querySelectorAll('li')
const list = document.querySelector('ul')

// Xóa 1 element bằng cách gọi method remove từ chính element
lis[0].remove()

// Hoặc gọi method removeChild từ element cha của nó
list.removeChild(lis[3])

// CLASSES & thuộc tính
const firstLi = document.querySelector('li:first-child')
const link = firstLi.children[0]

let val

// Classes
val = link.className
val = link.classList
val = link.classList[0]
link.classList.add('test')
link.classList.remove('test')
val = link

// Thuộc tính
val = link.getAttribute('href')
val = link.setAttribute('href', 'http://google.com')
link.setAttribute('title', 'Google')
val = link.hasAttribute('title')
link.removeAttribute('title')
val = link

console.log(val)

Mouse Event và Event Object

1
2
3
4
5
document.querySelector('.clear-tasks').addEventListener('click', function (e) {
  console.log('Hello World')
  // Dừng sự kiện mặc định, ví dụ submit form
  e.preventDefault();
})
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
document.querySelector('.clear-tasks').addEventListener('click', onClick)

function onClick(e) {
  console.log('Clicked');

  let val

  val = e

  // Lấy element
  val = e.target
  // Lấy id
  val = e.target.id
  // Lấy danh sách class
  val = e.target.className
  // Cũng lấy danh sách class nhưng trả về dạng object
  val = e.target.classList

  // Loại event
  val = e.type

  // Timestamp
  val = e.timeStamp

  // Tạo độ tương đối của event đối với cửa sổ window
  val = e.clientY
  val = e.clientX

  // Tọa độ tương đối của event đối với element
  val = e.offsetY
  val = e.offsetX

  console.log(val)
}

Keyboard Event

onkeydown

onkeydown thực thi khi người dùng ấn 1 phím

  • Sử dụng trong HTML
1
<input type="text" onkeydown="myFunction()" />
  • Sử dụng trong Javascript
1
2
3
object.addEventListener('keydown', (event) => {
  console.log(event.target.value) // sẽ bị chậm 1 nhịp khi log value
})

onkeypress

  • onkeypress thực thi khi người dùng ấn 1 phím.

  • Lưu ý: onkeypress không phải lúc nào cũng thực thi tất cả các phím (ví dụ: Alt, Ctrl, Shift, Esc) trên mọi trình duyệt. Để nhận biết người dùng nhấn bất kì phím nào thì nên dùng onkeydown, bởi vì nó hoạt động tất cả các phím.

  • Sử dụng trong HTML

1
<input type="text" onkeypress="myFunction()" />
  • Sử dụng trong Javascript
1
2
3
object.addEventListener('keypress', (event) => {
  console.log(event.target.value) // sẽ bị chậm 1 nhịp khi log value
})

onkeyup

  • onkeyup thực thi khi người dùng nhả 1 phím sau khi nhấn.
  • Sử dụng trong HTML
1
<input type="text" onkeyup="myFunction()" />
  • Sử dụng trong Javascript
1
2
3
object.addEventListener('keyup', (event) => {
  console.log(event.target.value) // Không bị chậm 1 nhịp khi log value
})

oninput

  • oninput thực thi khi value của input hoặc textarea thay đổi.
  • Mẹo: Sự kiện này tương tự như onchange. Sự khác nhau là oninput thực thi ngay lập tức sau khi value của element thay đổi, trong khi đó thì onchange thực thi khi element mất focus, sau khi content được thay đổi. Một sự khác nhau khác nữa là onchange cũng hoạt động được trên select element.
  • Sử dụng trong HTML
1
<input type="text" oninput="myFunction()" />
  • Sử dụng trong Javascript
1
2
3
object.addEventListener('input', (event) => {
  console.log(event.target.value) // Không bị chậm 1 nhịp khi log value
})

onchange

  • onchange thực thi khi value của một element được thay đổi.
  • Với radiobutton và checkbox, onchange thực thi khi trạng thái thay đổi. Với input, textarea thì thực thi value thay đổi và element mất focus.
1
<input type="text" onchange="myFunction()" />
  • Sử dụng trong Javascript
1
2
3
object.addEventListener('change', (event) => {
  console.log(event.target.value)
})

Cookie cho phép bạn lưu trữ thông tin người dùng website vào máy tính của bạn.

Tạo một cookie với Javascript

Javascript có thể tạo, đọc, xóa các cookie với thuộc tính document.cookie

Một cookie có thể được tạo như thế này

1
document.cookie = 'username=John Doe'

Bạn cũng có thể thêm thời gian hết hạn (định dạng UTC). Vì mặc định cookie sẽ bị xóa khi trình duyệt đóng.

1
document.cookie = 'username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC'

Với tham số path, bạn có thể nói cho trình duyệt path mà cookie thuộc về. Mặc định thì cookie thuộc về trang web hiện tại

1
2
document.cookie =
  'username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/'

Đọc một cookie với javascript

1
var x = document.cookie

document.cookie sẽ return tất cả cookie trong 1 chuỗi kiểu như: cookie1=value; cookie2=value; cookie3=value;

Thay đổi một cookie thì tương tự như cách ta tạo ra nó, giá trị cũ sẽ bị thay thế

1
2
document.cookie =
  'username=John Smith; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/'

Xóa một cookie

Xóa cookie bằng Javascript rất đơn giản, chỉ cần set thời gian hết hạn về giá trị như dưới đây

1
document.cookie = 'username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'

Lưu ý:

  • Nếu bạn set một cookie mới, tất cả các cookie cũ sẽ không bị thay thế. Cookie mới sẽ được thêm vào document.cookie.
  • Cookie được lưu theo trang web. Ví dụ bạn ở trang doidev.com, bạn không thể truy cập đến cookie của trang google.com được lưu trên máy bạn.
  • Nếu chỉ dùng các phương thức và thuộc tính có sẵn thì xử lý cookie không tiện lắm. Thường thì mọi người sẽ viết một function để tiện cho việc thao tác hoặc đơn giản là dùng thư viện js-cookie

Local Storage

Tương tự Cookie, local storage là một Web API giúp bạn lưu data website vào máy tính của bạn. Local storage tồn tại mãi mãi cho đến khi nó bị xóa.

Cú pháp siêu đơn giản, dễ dùng hơn Cookie nhiều

1
2
3
4
5
6
7
8
// thêm item
localStorage.setItem('name', 'John Doe')
// đọc item
localStorage.getItem('name') // 'John Doe'
// xóa item
localStorage.removeItem('name')
// xóa hết local storage
localStorage.clear()