Series 3 — JavaScript Fundamentals

 

JavaScript → DOM → Async → API

 

Bài 1. JavaScript & DOM – Từ ngôn ngữ lập trình đến tương tác với giao diện Web

 

1. JavaScript là gì?

Trong quá trình xây dựng website, HTML được sử dụng để tạo cấu trúc nội dung, CSS đảm nhiệm việc trình bày giao diện, còn JavaScript giúp website xử lý logic và phản hồi thao tác của người dùng.

Ví dụ, với một nút bấm đăng ký, HTML tạo ra nút bấm, CSS định dạng màu sắc và kích thước, còn JavaScript có thể xử lý sự kiện khi người dùng nhấn nút, kiểm tra dữ liệu và hiển thị thông báo.

JavaScript là ngôn ngữ lập trình cho phép xây dựng logic và hành vi tương tác cho website. Ngoài trình duyệt, JavaScript còn có thể chạy ở môi trường server thông qua các runtime như Node.js.

Khi JavaScript chạy trên trình duyệt, trình duyệt sử dụng JavaScript Engine để phân tích và thực thi mã nguồn. JavaScript cũng có thể sử dụng những Web API do môi trường trình duyệt cung cấp, chẳng hạn như DOM API, Timer API và Fetch API.

Cần phân biệt JavaScript với DOM: JavaScript là ngôn ngữ lập trình, còn DOM là mô hình biểu diễn tài liệu HTML mà JavaScript có thể truy cập và thay đổi.

2. Những kiến thức nền tảng của JavaScript

2.1. Variables – Biến trong JavaScript

Biến được sử dụng để lưu trữ và tham chiếu đến dữ liệu trong chương trình.

JavaScript có ba từ khóa khai báo biến thường gặp là var, let và const.

let userName = "Khoa";
const age = 21;
var isStudent = true;
  • let: khai báo biến có thể được gán lại.
  • const: khai báo biến không thể được gán lại sau khi khởi tạo.
  • var: cách khai báo biến cũ, có phạm vi theo hàm hoặc phạm vi toàn cục nếu khai báo ngoài hàm.

Trong JavaScript hiện đại, nên ưu tiên const khi không cần gán lại biến và dùng let khi giá trị cần thay đổi. var vẫn tồn tại trong nhiều đoạn mã cũ nhưng có một số đặc điểm về phạm vi và hoisting dễ gây nhầm lẫn.

Một lưu ý quan trọng: const không làm cho object hoặc array trở thành bất biến. Nó chỉ ngăn việc gán lại biến sang một giá trị khác.

const user = {
  name: "Khoa"
};

user.name = "Minh"; // Hợp lệ
// user = {};       // Không hợp lệ

2.2. Data Types – Kiểu dữ liệu

JavaScript có các kiểu dữ liệu nguyên thủy phổ biến:

  • string: chuỗi ký tự.
  • number: số.
  • boolean: giá trị true hoặc false.
  • undefined: biến đã được khai báo nhưng chưa có giá trị được gán.
  • null: biểu thị sự vắng mặt của giá trị theo chủ đích.
  • bigint: số nguyên lớn.
  • symbol: giá trị định danh duy nhất.

Ngoài ra, JavaScript còn có object, bao gồm object thông thường, array và function.

const name = "Khoa";
const score = 8.5;
const isActive = true;
const skills = ["HTML", "CSS", "JavaScript"];

const user = {
  name: "Khoa",
  age: 21
};

Hiểu kiểu dữ liệu giúp chúng ta xử lý dữ liệu chính xác hơn, đặc biệt khi lấy dữ liệu từ form, thao tác với array hoặc nhận dữ liệu từ API.

2.3. Operators – Toán tử

Toán tử cho phép thực hiện phép tính, so sánh và kết hợp các điều kiện.

const a = 10;
const b = 5;

console.log(a + b);  // 15
console.log(a > b);  // true
console.log(a === b); // false

Một điểm cần chú ý là === so sánh cả giá trị lẫn kiểu dữ liệu, trong khi == có thể chuyển đổi kiểu trước khi so sánh.

console.log(5 == "5");  // true
console.log(5 === "5"); // false

Trong đa số trường hợp, nên ưu tiên === và !== để tránh những kết quả khó đoán do ép kiểu ngầm.

2.4. Control Flow – Điều khiển luồng chương trình

Các câu lệnh điều kiện giúp chương trình lựa chọn cách xử lý dựa trên dữ liệu.

const score = 8;

if (score >= 8) {
  console.log("Tốt");
} else if (score >= 5) {
  console.log("Đạt");
} else {
  console.log("Cần cố gắng");
}

Vòng lặp giúp thực hiện một công việc nhiều lần mà không phải viết lại cùng một đoạn mã.

const products = ["Laptop", "Mouse", "Keyboard"];

for (let i = 0; i < products.length; i++) {
  console.log(products[i]);
}

Bên cạnh vòng lặp for, JavaScript còn cung cấp while, for...of, for...in và nhiều phương thức xử lý array như forEach().

Việc lựa chọn cấu trúc phù hợp giúp mã nguồn dễ đọc, dễ kiểm tra và giảm lỗi.

2.5. Functions – Hàm trong JavaScript

Hàm giúp đóng gói một nhóm lệnh thành đơn vị có thể tái sử dụng.

function calculateTotal(price, quantity) {
  return price * quantity;
}

const total = calculateTotal(100, 3);

console.log(total); // 300

Trong ví dụ này:

  • price và quantity là tham số.
  • 100 và 3 là đối số khi gọi hàm.
  • return trả kết quả về nơi gọi hàm.

JavaScript cũng hỗ trợ function expression và arrow function:

const calculateTotal = (price, quantity) => {
  return price * quantity;
};

Hàm là nền tảng quan trọng để tổ chức logic, xử lý sự kiện và quản lý các thao tác với dữ liệu.

2.6. Arrays & Objects – Tổ chức dữ liệu

Array dùng để lưu trữ một danh sách các giá trị, trong khi object giúp biểu diễn một thực thể với các thuộc tính cụ thể.

const products = [
  { id: 1, name: "Laptop", price: 15000000 },
  { id: 2, name: "Mouse", price: 300000 }
];

Có thể sử dụng map() để chuyển đổi dữ liệu:

const productNames = products.map(product => product.name);

console.log(productNames);
// ["Laptop", "Mouse"]

Hoặc dùng filter() để lọc dữ liệu:

const expensiveProducts = products.filter(
  product => product.price > 1000000
);

Các phương thức như map(), filter(), find(), some() và reduce() thường được sử dụng trong các ứng dụng Frontend để xử lý danh sách dữ liệu.

3. Từ JavaScript đến DOM

3.1. DOM là gì?

DOM là viết tắt của Document Object Model. Đây là mô hình biểu diễn tài liệu HTML dưới dạng cấu trúc đối tượng mà JavaScript có thể truy cập và thao tác.

Khi trình duyệt phân tích HTML, nó xây dựng một cấu trúc DOM gồm các node đại diện cho những thành phần trong tài liệu, chẳng hạn như phần tử, văn bản và thuộc tính.

Ví dụ, với HTML:

<body>
  <h1>Xin chào</h1>
  <p>Đây là website của tôi.</p>
</body>

Có thể hình dung cấu trúc DOM như sau:

document
└── html
    └── body
        ├── h1
        │   └── "Xin chào"
        └── p
            └── "Đây là website của tôi."

Đây là sơ đồ khái quát để dễ hình dung, không phải toàn bộ cấu trúc tài liệu HTML thực tế.

Nhờ DOM, JavaScript có thể tìm một phần tử, thay đổi nội dung, cập nhật class CSS, tạo phần tử mới hoặc phản hồi thao tác của người dùng.

3.2. Truy cập phần tử DOM

Để thao tác với một phần tử HTML, trước tiên chúng ta cần tìm phần tử đó.

Giả sử HTML có:

<h1 id="title">Xin chào</h1>
<p class="description">Nội dung ban đầu</p>

JavaScript có thể truy cập các phần tử bằng querySelector():

const title = document.querySelector("#title");
const description = document.querySelector(".description");

querySelector() nhận một CSS selector và trả về phần tử đầu tiên phù hợp. Nếu không tìm thấy, phương thức trả về null.

Ngoài ra, còn có querySelectorAll() để lấy tất cả phần tử phù hợp dưới dạng một NodeList.

Việc hiểu CSS selector từ những bài HTML và CSS trước đó sẽ giúp bạn sử dụng DOM dễ dàng hơn.

3.3. Thay đổi nội dung HTML

Sau khi lấy được phần tử, chúng ta có thể thay đổi nội dung hiển thị.

const title = document.querySelector("#title");

title.textContent = "Chào mừng đến với JavaScript";

textContent thay đổi nội dung văn bản của phần tử.

Trong khi đó, innerHTML cho phép đọc hoặc thiết lập nội dung HTML bên trong phần tử:

const description = document.querySelector(".description");

description.innerHTML = "<strong>Nội dung được in đậm</strong>";

Cần cẩn thận khi đưa dữ liệu do người dùng cung cấp vào innerHTML, vì nội dung không đáng tin cậy có thể tạo ra nguy cơ Cross-Site Scripting (XSS). Nếu chỉ cần hiển thị văn bản, nên ưu tiên textContent.

3.4. Thay đổi CSS bằng JavaScript

JavaScript có thể thay đổi class của phần tử thông qua classList.

<p id="message">Xin chào!</p>
const message = document.querySelector("#message");

message.classList.add("active");
message.classList.remove("hidden");
message.classList.toggle("highlight");

Các phương thức thường dùng:

  • add(): thêm class.
  • remove(): xóa class.
  • toggle(): thêm class nếu chưa có, hoặc xóa nếu đã có.
  • contains(): kiểm tra phần tử có class hay không.

Thông thường, nên định nghĩa kiểu dáng trong CSS và dùng JavaScript để thay đổi class. Cách này giúp tách biệt logic và giao diện, đồng thời dễ bảo trì hơn việc sửa trực tiếp nhiều thuộc tính CSS bằng JavaScript.

3.5. Tạo và xóa phần tử DOM

JavaScript không chỉ thay đổi những phần tử đã tồn tại mà còn có thể tạo phần tử mới.

const list = document.querySelector("#product-list");

const item = document.createElement("li");
item.textContent = "Laptop";

list.appendChild(item);

Đoạn mã trên tạo một phần tử li, đặt nội dung văn bản và thêm nó vào phần tử có id="product-list".

Để xóa một phần tử, có thể sử dụng:

item.remove();

Những thao tác này hữu ích khi xây dựng danh sách sản phẩm, thông báo, Todo List hoặc nội dung được tạo ra từ dữ liệu.

4. Events – Xử lý sự kiện

4.1. Event là gì?

Event là một sự kiện xảy ra trong trình duyệt, chẳng hạn như người dùng nhấn nút, nhập dữ liệu, gửi form hoặc di chuyển chuột.

JavaScript có thể đăng ký hàm xử lý để phản hồi những sự kiện này.

Ví dụ:

<button id="hello-button">Nhấn vào đây</button>
<p id="result"></p>
const button = document.querySelector("#hello-button");
const result = document.querySelector("#result");

button.addEventListener("click", () => {
  result.textContent = "Bạn vừa nhấn nút!";
});

Khi người dùng nhấn nút, sự kiện click xảy ra và hàm callback được thực thi để cập nhật nội dung đoạn văn.

4.2. addEventListener()

addEventListener() là phương thức phổ biến để đăng ký một hàm xử lý sự kiện cho phần tử.

element.addEventListener("eventName", callback);

Trong đó:

  • element: phần tử cần lắng nghe sự kiện.
  • eventName: tên sự kiện, ví dụ click, input, submit.
  • callback: hàm được thực thi khi sự kiện xảy ra.

Một ưu điểm của addEventListener() là có thể đăng ký nhiều hàm xử lý cho cùng một loại sự kiện trên cùng phần tử.

4.3. Event Object

Hàm callback có thể nhận một đối tượng sự kiện chứa thông tin về sự kiện vừa xảy ra.

button.addEventListener("click", (event) => {
  console.log(event.type);
  console.log(event.target);
});

Một số thuộc tính quan trọng:

  • event.type: loại sự kiện.
  • event.target: phần tử nơi sự kiện bắt đầu.
  • event.currentTarget: phần tử đang được gắn hàm xử lý sự kiện.

Phân biệt target và currentTarget đặc biệt hữu ích khi làm việc với các phần tử lồng nhau hoặc event delegation.

4.4. Event Bubbling và Event Delegation

Trong DOM, nhiều sự kiện có cơ chế bubbling: sự kiện bắt đầu từ phần tử phát sinh rồi có thể lan lên các phần tử cha.

Ví dụ, thay vì gắn một event listener cho từng nút xóa trong danh sách sản phẩm, chúng ta có thể gắn một listener cho phần tử cha và xác định nút được nhấn.

Đây là kỹ thuật gọi là event delegation.

const list = document.querySelector("#product-list");

list.addEventListener("click", (event) => {
  const button = event.target.closest(".delete-button");

  if (!button || !list.contains(button)) {
    return;
  }

  button.closest(".product-item")?.remove();
});

Trong ví dụ này, khi người dùng nhấn một nút có class delete-button, JavaScript tìm phần tử sản phẩm tương ứng và xóa nó.

Event delegation giúp giảm số lượng listener cần đăng ký, đặc biệt hữu ích với danh sách có nhiều phần tử hoặc nội dung được tạo động.

4.5. Xử lý Form

JavaScript có thể lắng nghe sự kiện submit để xử lý dữ liệu biểu mẫu.

<form id="register-form">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>
  <button type="submit">Đăng ký</button>
</form>
const form = document.querySelector("#register-form");

form.addEventListener("submit", (event) => {
  event.preventDefault();

  const formData = new FormData(form);
  const email = formData.get("email");

  console.log(email);
});

preventDefault() ngăn hành vi mặc định của trình duyệt trong sự kiện gửi form, cho phép JavaScript tự xử lý bước tiếp theo.

Tuy nhiên, việc ngăn gửi form không thay thế validation. Với dữ liệu quan trọng, ứng dụng vẫn cần kiểm tra dữ liệu ở phía server.

5. Kết hợp JavaScript và DOM trong thực tế

Hãy xem xét một ví dụ nhỏ: xây dựng chức năng thêm công việc vào Todo List.

<form id="todo-form">
  <input id="todo-input" type="text" placeholder="Nhập công việc" required>
  <button type="submit">Thêm</button>
</form>

<ul id="todo-list"></ul>
const form = document.querySelector("#todo-form");
const input = document.querySelector("#todo-input");
const list = document.querySelector("#todo-list");

form.addEventListener("submit", (event) => {
  event.preventDefault();

  const task = input.value.trim();

  if (!task) {
    return;
  }

  const item = document.createElement("li");
  item.textContent = task;

  list.appendChild(item);
  input.value = "";
});

Đoạn mã trên kết hợp nhiều kiến thức đã học:

  1. Truy cập phần tử bằng querySelector().
  2. Lắng nghe sự kiện gửi form bằng addEventListener().
  3. Ngăn hành vi mặc định bằng preventDefault().
  4. Đọc và kiểm tra dữ liệu nhập vào.
  5. Tạo phần tử bằng createElement().
  6. Cập nhật giao diện bằng appendChild().

Đây là một ví dụ đơn giản về cách JavaScript biến một trang HTML tĩnh thành giao diện có khả năng tương tác.

Trong ứng dụng thực tế, Todo List có thể được mở rộng với chức năng xóa công việc, đánh dấu hoàn thành, lọc danh sách và lưu dữ liệu vào localStorage.

6. Những khái niệm cần tiếp tục tìm hiểu

Sau khi nắm vững các kiến thức trên, bạn có thể tiếp tục đào sâu những khái niệm thường gặp khi làm việc với JavaScript:

  • Scope và Closure: hiểu phạm vi biến và cách hàm có thể giữ quyền truy cập đến môi trường bên ngoài.
  • Hoisting: hiểu cách khai báo biến và hàm được xử lý trước khi thực thi mã.
  • this, call, apply và bind: tìm hiểu cách xác định ngữ cảnh thực thi của hàm.
  • Prototype và Class: hiểu cơ chế kế thừa trong JavaScript.
  • Array Methods: sử dụng thành thạo map(), filter(), reduce(), find() và các phương thức liên quan.

Đây là những kiến thức giúp bạn đọc mã nguồn JavaScript tốt hơn, hiểu cách các thư viện hoạt động và chuẩn bị cho việc học React hoặc Next.js.

Kết luận

JavaScript cung cấp logic và khả năng xử lý dữ liệu cho website, trong khi DOM cho phép JavaScript truy cập và cập nhật cấu trúc HTML mà trình duyệt hiển thị. Khi kết hợp với Events, chúng ta có thể tạo ra những giao diện phản hồi trực tiếp với thao tác của người dùng.

Đối với Frontend Developer, điều quan trọng không chỉ là ghi nhớ cú pháp mà còn phải hiểu cách dữ liệu được xử lý, cách phần tử DOM được cập nhật và cách các sự kiện kích hoạt logic chương trình.

Sau khi nắm vững JavaScript và DOM, bước tiếp theo là tìm hiểu Asynchronous JavaScript & API: cách xử lý những tác vụ bất đồng bộ, gửi yêu cầu đến server và hiển thị dữ liệu thực tế từ API lên giao diện website.

Bài viết khác

PHP & Laravel Fundamentals

PHP là gì? — Ngôn ngữ lập trình phía máy chủ và vai trò trong phát triển web PHP là gì?   PHP (viết tắt của PHP: Hypertext Preprocessor) là một ngôn ngữ lập trình phía máy chủ, thường được sử dụng để xây dựng website và ứng dụng web động. Trong mô hình phát […]

OpenAPI / Swagger

1. OpenAPI và Swagger là gì? Khi xây dựng backend bằng Go và Gin, developer thường định nghĩa các endpoint như GET /users, POST /users hoặc GET /users/{id}. Tuy nhiên, việc có endpoint hoạt động không đồng nghĩa với việc người khác biết cách sử dụng chúng. OpenAPI là gì? OpenAPI Specification (OAS) là một […]

Gin-Gonic Fundamentals

1. Gin-Gonic là gì? Gin, thường được gọi là Gin-Gonic, là một HTTP web framework viết bằng Go. Framework này cung cấp các công cụ để xây dựng web server và REST API mà không cần tự triển khai toàn bộ việc định tuyến request, đọc dữ liệu HTTP, tạo response hay quản lý middleware. […]

Test Case & Test Plan & Test Data

1. Lời mở đầu và vị trí nền tảng của bộ ba quản lý kiểm thử trong dự án phần mềm Trong quy trình sản xuất phần mềm hiện đại, nếu hoạt động lập trình (Coding) là việc hiện thực hóa các ý tưởng thiết kế thành sản phẩm chạy được, thì hoạt động kiểm […]

JWT & Token Authentication

Token Authentication, JWT, Access Token và Refresh Token 1. Token Authentication là gì?   Token Authentication là cơ chế xác thực sử dụng token để chứng minh danh tính hoặc thông tin xác thực của người dùng hay client khi truy cập các tài nguyên được bảo vệ. Sau khi người dùng đăng nhập thành […]

Authentication & Authorization

Authentication & Authorization là gì? Authentication (xác thực) và Authorization (phân quyền) là hai khái niệm quan trọng trong bảo mật hệ thống và API. Nói một cách đơn giản: Authentication: Xác minh bạn là ai. Authorization: Xác định bạn được phép làm gì. Ví dụ, khi đăng nhập vào một hệ thống quản lý […]

Leave a Reply

Your email address will not be published. Required fields are marked *