Bản chất của việc tìm thẻ theo tên class trong Javascript

Đây là một câu hỏi khá thú vị ở group học lập trình web, nội dung câu hỏi như sau: "Các pro cho mình hỏi, mình tạo biến toàn cục như hình, nhưng ko hiểu sao nó ko nhận được giá trị gán vào".

Mình sẽ không đăng hình mà sẽ đăng code luôn nhé.

File HTML:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <link rel="stylesheet" type="text/css" href="demo.css"/>
        <script type="text/javascript" src="demo.js"></script>
    </head>
    <body>
        <section id="container">
            <div class="bg" id="led-light"></div>		
            <div class="bg" id="zone-light">Press here</div>
        </section>
    </body>
</html>

File Javascript:

var led = document.getElementById("led-light");
var zone = document.getElementById("zone-light");
var bg = document.getElementsByClassName("bg");

window.onload = function graphic() {
    console.log(bg); // Có kết quả
    console.log(zone); // Không có kết quả
};

Ý của bạn ấy muốn hỏi tạo sao trong sự kiện onload biến bg lại có kết quả và biến zone lại không có? 

Bài giải

-------------------- ######## --------------------

Đúng với cái tiêu đề mà mình đang soạn, để giải thích được vấn đề này thì bạn cần phải hiểu nguyên tắc hoạt động của trình biên dịch. Theo nguyên tắc hoạt động thì khi trang web được load trình biên dịch sẽ chạy từ trên xuống và từ trái qua phải, nếu gặp mã JS thì nó sẽ biên dịch mã JS rồi mới tiếp tục biên dịch phía dưới.

Trong ví dụ bạn sử dụng external đặt ở thẻ head và bạn có sử dụng sự kiện onload là quá thông minh rồi bởi đây là sự kiện khi tài liệu HTML được xử lý xong thì nó mới chạy, tuy nhiên sai lầm của bạn là lại gán các biến nằm bên ngoài sự kiện này.

# Quay lại câu hỏi của bạn

Ok ta quay lại câu hỏi của bạn.

Tại sao biến zone lại có giá trị là null?

Biến zone bạn khai báo ở bên ngoài hàm onload và bạn lại đặt file JS ở trên thẻ head nên rõ ràng đối tượng mà biến này đang trỏ tới không tồn tại.

Đối với truy vấn theo ID thì lúc bạn truy vấn nếu không tìm thấy thì trình biên dịch sẽ gắn cờ là không tìm thấy, vì vậy trong trường hợp này biến zone sẽ có giá trị NULL là chuẩn xác.

Tại sao biến bg lại có giá trị?

Khác với việc truy vấn theo ID, khi bạn truy vấn theo class thì trình biên dịch sẽ hiểu là kết quả trả về sẽ là một mảng các đối tượng vì trong tài liệu HTML cho phép tạo nhiều class. Như vậy với biến zone lúc khởi tạo sẽ có giá trị là một mảng rỗng, tuy nhiên trong quá trình biên dịch tài liệu nếu gặp thẻ nào có class là bg thì nó sẽ gán vào mảng, vì vậy sau khi biên dịch xong tài liệu bạn sẽ nhận được danh sách đầy đủ các thẻ HTML cần lấy.

Bạn cần một minh chứng?

Ok mình sẽ viết lại đoạn JS như sau:

var bg = document.getElementsByClassName("bg");
console.log(bg); // là mảng rỗng

window.onload = function graphic() {
    console.log(bg); // Có kết quả
};

Với đoạn code này bạn sẽ thấy kết quả nó in ra sẽ như sau:

Rõ ràng ở lần console đầu tiên kết quả là mảng rỗng và lần console thứ hai nằm trong sự kiện onload là mảng đầy đủ các phần tử.

Bây giờ bạn sửa lại file HTML như sau:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <link rel="stylesheet" type="text/css" href="demo.css"/>
        <script type="text/javascript" src="demo.js"></script>
    </head>
    <body>
        <section id="container">
            <div class="bg" id="led-light"></div>	
            <script>
                console.log(bg);
            </script>
            <div class="bg" id="zone-light">Press here</div>
        </section>
    </body>
</html>

Trong file HTML này mình đã bổ sung một đoạn code:

<script>
    console.log(bg);
</script>

Nếu chiếu theo quy tắc mà mình đã trình bày ở trên thì kết quả sẽ in ra như sau:

  • Console lần 1: mảng rỗng
  • Console lần 2 nằm trong file HTML: mảng có 1 phần tử
  • Console lần 3 nằm trong sự kiện onload: mảng có 2 phần tử

Chạy thử xem đúng không nào?

Kết quả quá chuẩn.

# Tổng kết

Như vậy khi bạn truy vấn theo ID thì tại thời điểm thực hiện truy vấn nếu như trong bộ nhớ của trình duyệt chưa tìm thấy đối tượng thì kết quả truy vấn sẽ trả về là NULL.

Khi bạn truy vấn theo class thì ban đầu nó sẽ tìm trong bộ nhớ của trình duyệt đã tìm thấy các thẻ chứa class đó chưa? Nếu có thì nó sẽ gán vào mảng và không có thì nó sẽ trả về một mảng rỗng, sau đó trong quá trình biên dịch nếu xuất hiện thêm class đó thì nó sẽ tiếp tục gán vào kết quả.

Câu hỏi và bài tập

  • Bản chất của việc tìm thẻ theo tên class trong Javascript

Nguồn: freetuts.net