Javascript

DOM in JS #4: Manipulating elements

DOM in JS 4 Manipulating elements

1. Mở bài

Như trong các phần trước mình cũng đã có giới thiệu đến các bạn những điều cơ bản nhất về DOM, những các quy query của nó qua phần 2phần 3 thì trong phần này mình cũng sẽ giới thiệu các cách để modifier DOM thêm, sửa, xóa, … Đây đều là các phương thức rất quan trọng, qua phần này mong bạn đọng lại được điều gì đó và hiểu thêm một chút về cơ chế hoạt động của DOM, từ đó có được một cái base vững chắc hơn nữa để có thể đi xa hơn với JS.

2. Chi tiết

createElement() method:

Phương thức đầu tiên mà mình muốn giới thiệu với các bạn đó là createElement(), phương thức này sẽ nhận đầu vào là một htmlTag và return ra một node với type là Element hay bạn cũng hiểu là nó return ra một Element (Như phần một mình đã có nói Element cũng chỉ là một type của Node).

let element = document.createElement(htmlTag);

Sau khi tạo Element này sẽ được append vào DOM tree, sau đây là ví dụ, hoặc ngay bây giờ bạn có thể F12 lên vì thử nhé (Mình nghĩ là vừa làm vừa thử bạn sẽ nhớ được nhiều hơn đó.) Hãy paste đoạn code bên dưới xuống cuối cùng rồi Enter sau đó kéo xuống cuối trang nhé.

let div = document.createElement('div');
div.innerHTML = '<p>chamdev.com</p>';
document.body.appendChild(div);

appendChild() method:

Như bên trên mình có dùng phương thức này để append một Node vào body element và theo mô tả đây là phương thức nhận vào một Node (có thể là comment, text, element, … nếu bạn không hiểu có thể quay lại bài đầu nhé.) và add nó vào vị trí cuối cùng của parentNode. Bạn có thể xem syntax dưới đây để hiểu thêm về cách làm.

parentNode.appendChild(childNode);

Đặc biệt: Nếu Node đã tồn tại mà bạn vẫn append nó vào parentNode thì Node này sẽ được move đến position cuối cùng của parentNode. Để hiểu hơn bạn hãy bật ngay trình duyệt F12 đánh một Element có id là gì đó rồi thử nhé.

let aEl = document.getElementById("a");
document.body.appendChild(aEl);

Ngoài ra còn có một phương thức cũng same same phương thức này nên mình nhóm nó lại nhé. Đó chính là append() bạn có thể tham khảo thêm ở bảng bên giới:

Differencesappend()appendChild()
Return valueundefinedThe appended Node object
InputMultiple Node ObjectsA single Node object
Parameter TypesAccept Node and DOMStringOnly Node

(Ở đây bạn có thể hiểu DOMString chính là nodeType = 3 ( TEXT_NODE ) bạn có thể tham khảo lại bài đầu tiên mình viết nhé).

textContent and innerText property:

Đây là thuộc tính cho bạn text của Node cũng như text của tất cả nhũng thằng con của node mà bạn chỉ định. Thuộc tính này không hề quan tâm đến Style miễn là có text trong Element thì nó sẽ được lấy ra. Dưới đây là syntax:

let text = node.textContent;

Một lần nữa bạn hãy mở trình duyệt lên và kiểm tra, hãy thêm id vào thẻ, thêm 1 đoạn comment và một thẻ có style được ẩn đi sau đó query nó ra và xem nhé.

<div id="note">
    JavaScript textContent Demo!
    <span style="display:none">Hidden Text!</span>
    <!-- my comment -->
</div>    

<script>
    let text = document.getElementById("note").textContent;
    console.log(text); // JavaScript textContent Demo!
                       // Hidden Text!
</script>

Đối với innerText cũng giống như textContent, tuy có một điểm khác là là nó chỉ có thể lấy ra được các phần mà chúng ta có thể xem được (bạn có thể hiển là không bị style css ẩn đi). Với ví dụ như bên trên khi dùng với innerText kết quả sẽ cho ra khác một chút.

<div id="note">
    JavaScript textContent Demo!
    <span style="display:none">Hidden Text!</span>
    <!-- my comment -->
</div>    

<script>
    let text = document.getElementById("note").innerText;
    console.log(text); // JavaScript textContent Demo!
</script>

Ngoài ra với hay thuộc tính này bạn cũng có thể set lại text cho parentNode, dưới đây là syntax nhé. Bạn có thể F12 lên và thử ngay và luôn.

document.body.textContent = "chamdev.com";
// or
document.body.innerText = "chamdev.com";

innerHTML property:

Đây là thuộc tính dùng để lấy ra Element cũng như là gắn Element cho parentNode và syntax của nó sẽ được viết như thế này:

let content = element.innerHTML; // get
element.innerHTML = newHTML; // set

Như vậy bên trên là 2 trường hợp mà bạn có thể dùng innerHTML để get hoặc set, bạn có thể liên tưởng ngay đến 2 thuộc tính bên trên textContent và innerText, tuy nhiên innerHTML thì có thể gắn HTML. Và cũng chính vì nó có thể gắn HTML lên có thể gắn cả thẻ script vào web – đây chính là một điều nguy hiểm mà phần này mình muốn nhấn mạnh. Lỗi này thường được biết đến với cái tên XSS (Cross Site Scripting) bạn có thể tham khảo thêm tại đây.

XSS là một lỗi cực kì nguy hiểm bạn có thể hiểu đơn giản là khi bạn chèn comment, message, …. hay cơ bản nhất là chèn 1 đoạn script vào web, sau đó load lên đoạn script này lại chạy. Và nếu kẻ xấu muốn điều hướng trang của bạn hay ghi nội dung xấu lên thì sao – đó, thấy nguy hiểm chưa nào. Lỗi này bắt nguồn từ viện bạn không validate các input đầu vào và cũng không để ý đầu ra bạn dùng thuộc tính nào textContent hay innerHTML. Tại sao mình lại nhấn mạnh 2 thuộc tính này, bời vì giả dụ nếu nhỡ như đã không validate input thì để tránh lỗi ít nhất bạn cũng lên dùng textContent chứ không nên dùng innerHTML để show content vì làm như vậy script sẽ được chạy. (Đây chỉ là 1 lỗi cơ bản, mình sẽ viết thêm những lỗi khác ở bài sau nhé).

Để hiểu phần mình nói ở trên hơn bạn có thể chèn đoạn script dưới vào console nhé. Và hãy để ý bao giờ tester cũng test case này ^^ bạn không muốn fix cũng phải fix (mà thực ra fix dễ mà). Nhưng nếu dùng framework thì nó tự làm cho chúng ta rồi còn nếu muốn enable nó lên thì search thôi ví dụ với react ^^.

let body = document.body;
body.innerHTML = `<img src='1' onerror='alert("Error loading image")'>`;

DocumentFragment interface:

DocumentFragment là một phiên bản nhỏ hơn của Document, tuy nhiên nó được định nghĩa không phải làm một phần hoạt động trong DOM do vậy nếu bạn change DocumentFragment  thì cũng không hề ảnh hưởng đến performance. Ở đây bạn có thể liên tưởng đến các thành phần React.Fragment hay ng-container nó cũng có hoạt động giống với DocumentFragment mặc dù viết nhưng mục đích chỉ là wrap những thanfnh phần bên trong lại và bên ngoài bạn hoàn toàn không nhìn thấy sự thay đổi nào.

Chúng ta có hay cách để tạo ra DocumentFragment đó là khởi tạo đối tượng DocumentFragment hoặc dùng phương thức bên trong document.

let fragment = new DocumentFragment();
// or
let fragment = document.createDocumentFragment();

Bây giờ bạn hãy mở trình duyệt và F12 thêm đoạn code này vào xem nhé. ^^ Nhớ là kéo xuống cuối trang nhé.

let languages = ['JS', 'TypeScript', 'Elm', 'Dart','Scala'];

let bodyEl = document.body;

let fragment = new DocumentFragment();
languages.forEach((language) => {
    let li = document.createElement('li');
    li.innerHTML = language;
    fragment.appendChild(li);
})

bodyEl.appendChild(fragment);

insertBefore() and insertAfter() method:

Phương thức insertBefore() sẽ insert một newNode vào trước một node đã có sẵn trong một list node con của một parentNode (@@), nghe thì hơi đau đầu nhưng hãy nhìn vào đoạn syntax sau nhé.

parentNode.insertBefore(newNode, existingNode);

Như ở đây bán có thể thấy chúng ta có một parentNode và newNode chính là phần tử cần add vào. Trong trường hợp existingNode là null thì phần tử được add vào này sẽ là phần tử cuối cùng của node (Dễ hiểu đúng không nào).

insertAfter() tương tự như insertBefore nhưng đảo ngược lại. (^^)

removeChild() method:

Đây là phương thức remove child bên trong một parentNode, nếu child node không có tồn tại trong parentNode thì phương thức này sẽ throw ra exception. Phương thức này cũng return ra node đã được remove trong trường hợp bạn muốn dùng lại. Hãy xin syntax đẻ hiểu rõ hơn.

let childNode = parentNode.removeChild(childNode); // Nếu bạn muốn dùng lại node đã remove
parentNode.removeChild(childNode); // Nếu bạn không muốn dùng lại Node đã remove

cloneNode() method:

Phương thưc này của Node cho phép bạn clone một node tương tự node mà bạn muốn clone, hãy xem syntax ở dưới đây để bạn có thể phần nào hiểu rõ hơn.

let clonedNode = originalNode.cloneNode(deep);

Đối với deep mặc định sẽ là false khi đó cloneNode() chỉ có thể clone được phần node trực tiếp được clone (nghĩa là chính bản thân nó). Nhưng nếu bạn set nó là true thì nó sẽ clone được tất cả các node con bên trong originalNode nữa. (Hãy mạnh mẽ F12 lên và kiểm tra nhé.)

let body = document.body;
let bodyCloneNormal = body.cloneNode();
let bodyCloneDeep = body.cloneNode(true);
console.log("body", body);
console.log("bodyCloneNormal", bodyCloneNormal);
console.log("bodyCloneDeep", bodyCloneDeep);

replaceChild() method:

Phương thức này cho phép bạn thay thế Node đã có sẵn với một Node mới, cái này nhìn khá đơn giản phải không nào.

parentNode.replaceChild(newChild, oldChild);

Nếu trong trường hợp không tồn tại oldChild bạn sẽ nhận được exception Uncaught TypeError: Failed to execute 'replaceChild' on 'Node': parameter 2 is not of type 'Node'.

3. Kết luận

Đây chỉ là một số các phương thức mình nghĩ khá là hay, tuy nhiên Dom còn cho chúng ta rất nhiều phương thức khác nữa, nếu bạn muốn biết thêm có thể tim hiểu ở đây.

Những thư viện hay các framework muốn hoạt động được, thì đều được base trên những cái rất cơ bản này. Đó chính là lí do vì sao mình viết phần này rất kĩ, cũng là để bản thân mình xem lại và cũng mong muốn bạn đọc nắm được một phần nào đó. Khi viết bài mình cũng chắc không thể tránh khỏi thiếu sót về kiến thức hay sai, nên mong nếu như bạn có thấy thì báo lại mình với nhé. Nếu bạn đã đọc đến đây thì một lần nữa cám ơn bạn rất rất nhiều <3.

Cuối cùng thì: SEE YOU SOONNNN!

4. Tham khảo

# JavaScript DOM
# JavaScript HTML DOM
# The HTML DOM Document Object

Tagged , , ,
0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments