Hình học mô hình đối tượng tài liệu (DOM): Giới thiệu và hướng dẫn cho người mới bắt đầu

theanh

Administrator
Nhân viên
Nếu bạn đã làm việc với JavaScript một thời gian, bạn có thể khá quen thuộc với tập lệnh DOM (Mô hình đối tượng tài liệu)CSSOM (Mô hình đối tượng CSS). Ngoài các giao diện được xác định bởi các thông số kỹ thuật DOM và CSSOM, một tập hợp con các phương thức và thuộc tính được chỉ định trong Mô-đun Chế độ xem CSSOM, cung cấp API để xác định và thao tác hình học phần tử DOM nhằm hiển thị giao diện người dùng thú vị trên web.

Điều kiện tiên quyết:
  • Ôn tập về Hệ tọa độ;
  • Hiểu biết về Bố cục và Định vị CSS;
  • Viết các lệnh gọi lại trong JavaScript;
  • Một chút kiên nhẫn.
Mục lục:
  • Mô-đun Chế độ xem CSSOM
  • Tại sao các Phương thức và Thuộc tính Hình học lại Quan trọng?
  • Hình học Nút Phần tử
  • Hình học cửa sổ và tài liệu
  • Tọa độ
  • Các trường hợp sử dụng

Mô-đun chế độ xem CSSOM​

Mô hình đối tượng CSS (CSSOM) là một tập hợp các API cho phép thao tác CSS từ JavaScript. Giống như DOM cung cấp giao diện để thao tác HTML, CSSOM cho phép tác giả đọc và thao tác CSS.

Chế độ xem CSSOM là một mô-đun CSS chứa một loạt các thuộc tính và phương thức, tất cả được đóng gói lại để cung cấp cho tác giả một giao diện riêng để lấy thông tin về chế độ xem trực quan của các thành phần. Các thuộc tính trong mô-đun này chủ yếu là chỉ đọc và được tính toán mỗi lần chúng được truy cập — các giá trị trực tiếp.

Hiện tại, Mô-đun Chế độ xem CSSOM chỉ là bản nháp đang hoạt động và đang được sửa đổi trong Bảng thông số kỹ thuật của W3C. Do đó, bản chất của nó là xác định các giao diện này, cả giao diện đã tồn tại và giao diện mới, theo cách có thể tương thích trên nhiều trình duyệt.

Tại sao các phương pháp và thuộc tính hình học lại quan trọng?​

Theo quan điểm của tôi, có một số lý do để thử hiểu và sử dụng các thuộc tính và phương thức Chế độ xem CSSOM.

Đầu tiên, không phải giao diện người dùng hàng ngày yêu cầu các thành phần có thể di chuyển để đạt được các câu chuyện người dùng cơ bản nhất của nó. Trừ khi bạn đang xây dựng giao diện trò chơi, bạn có thể không phải lúc nào cũng cần làm cho các thứ có thể di chuyển trên trang web của mình. Các thuộc tính hình học rất hữu ích mặc dù có những điều này vì khả năng thao tác theo chương trình chế độ xem trực quan của các phần tử DOM cung cấp cho các nhà phát triển nhiều siêu năng lực hơn để triển khai giao diện người dùng động.

Bảng Kanban được triển khai vì các thành phần có thể được kéo và thả ở các phần có liên quan. Nhiều nội dung hơn được tải khi người dùng cuộn xuống cuối tài liệu vì các giá trị vị trí cuộn có thể đọc được. Vì vậy, mặc dù có vẻ không rõ ràng ngay lập tức, nhưng thông qua việc biết thông tin chính xác về kích thước và vị trí của các phần tử mà các tính năng này có thể đạt được.

Thứ hai, khi xem tài liệu HTML trong trình duyệt web, các Phần tử DOM được hiển thị dưới dạng hình dạng trực quan, do đó, chúng có biểu diễn trực quan tương ứng mà trình duyệt có thể xem/trực quan. Truy cập các thuộc tính trực quan trực tiếp của các phần tử DOM này thông qua các thuộc tính và phương thức Chế độ xem CSSOM mang lại lợi thế hơn so với các thuộc tính CSS thông thường. Và sau đó bạn hỏi cách:
  1. Sau khi thiết lập các thuộc tính width và height của các phần tử HTML trong CSS, thuộc tính CSS box-sizing cuối cùng sẽ thiết lập cách tính tổng chiều rộng và chiều cao của phần tử. Điều này tạo ra JavaScript dễ bị lỗi nếu giá trị của box-sizing của chúng ta thay đổi.
  2. Thứ hai, hầu như không có cách nào để đọc giá trị số chính xác của chiều rộng của phần tử được đặt thành auto. Và đôi khi, chúng ta cần chiều rộng theo pixel và kích thước chính xác.
Cuối cùng, có vẻ linh hoạt và hữu ích hơn nhiều khi có một tập hợp các giá trị lives chỉ đọc có thể dựa vào khi viết một số mã khác để thao tác các phần tử dựa trên các giá trị live hiện tại.

Hình học nút phần tử​

Độ lệch​

Tọa độ được chỉ định bằng mô hình "độ lệch" sử dụng góc trên cùng bên trái của phần tử đang được kiểm tra hoặc nơi sự kiện đã xảy ra.
MDN
Không giống như các thuộc tính khác trong CSSOM View, các thuộc tính độ lệch chỉ khả dụng cho các nút HTMLElement bắt nguồn từ nút Element. Do đó, bạn không thể đọc các thuộc tính bù trừ của SVGElement vì chúng không tồn tại.


Offset Left và Top​
Sử dụng các thuộc tính chỉ đọc offsetLeftoffsetTop sẽ cung cấp tọa độ x/y của một phần tử so với offsetParent của nó. Thuộc tính offsetLeft trả về khoảng cách của đường viền ngoài bên trái của phần tử hiện tại so với đường viền trong bên trái của offsetParent trong khi thuộc tính offsetTop trả về khoảng cách của đường viền trên cùng bên ngoài của phần tử hiện tại so với đường viền trên cùng bên trong của offsetParent.
Offset Parent​
offsetParent của bất kỳ phần tử nào là phần tử tổ tiên gần nhất của nó có thuộc tính vị trí CSS không tĩnh, phần tử , hoặc [TABLE] hoặc ở phần gốc, phần tử .
Chiều rộng và chiều cao offset​
Các thuộc tính chỉ đọc này cung cấp kích thước bên ngoài đầy đủ của các nút phần tử. offsetWidth được xác định bằng cách tính tổng kích thước của đường viền dọc, phần đệm và nội dung của phần tử, bao gồm bất kỳ thanh cuộn nào có thể tồn tại. offsetHeight được tính theo cách tương tự bằng cách sử dụng đường viền ngang, phần đệm và chiều cao nội dung của phần tử.

Clients​



Client Left và Top​
Theo nghĩa cơ bản nhất, các thuộc tính chỉ đọc này cung cấp kích thước tính bằng pixel của chiều rộng đường viền trái và chiều rộng đường viền trên cùng của phần tử. Tuy nhiên, theo nghĩa sâu hơn, giá trị của các thuộc tính clientLeftclientTop của một phần tử cung cấp tọa độ tương đối của mặt trong (đệm ngoài) của phần tử đó so với mặt ngoài (viền ngoài) của nó.

Vì vậy, khi một tài liệu có hướng viết từ phải sang trái và thanh cuộn dọc bên trái, thì clientLeft sẽ trả về các giá trị tọa độ, bao gồm kích thước của thanh cuộn. Điều này là do thanh cuộn hiển thị giữa mặt trong (đệm ngoài) của phần tử đó so với mặt ngoài (viền ngoài).
Chiều rộng và chiều cao của Client​
Các thuộc tính clientWidthclientHeight chỉ đọc của một phần tử trả về kích thước của vùng bên trong đường viền của phần tử. Thuộc tính clientWidth sẽ trả về kích thước của chiều rộng nội dung của phần tử và phần đệm dọc của phần tử đó mà không có thanh cuộn. Nếu không có phần đệm, thì clientWidth chỉ là kích thước của chiều rộng nội dung của phần tử đó. Điều này cũng tương tự đối với thuộc tính clientHeight, thuộc tính này sẽ trả về kích thước của chiều cao nội dung của phần tử cộng với phần đệm ngang và nếu không có phần đệm nào, thuộc tính này sẽ chỉ trả về chiều cao nội dung là clientHeight.

Cuộn​



Cuộn sang trái và lên trên​
Một phần tử không có nội dung tràn trên trục x hoặc trục y của nó sẽ trả về 0 khi các thuộc tính scrollLeftscrollTop của nó được truy vấn tương ứng. Thuộc tính scrollLeft của một phần tử trả về khoảng cách tính bằng pixel mà nội dung của phần tử được cuộn theo chiều ngang, trong khi thuộc tính scrollTop cung cấp khoảng cách tính bằng pixel mà nội dung của phần tử được cuộn theo chiều dọc.

Các pixel được trả về bởi thuộc tính scrollLeftscrollTop của một phần tử không phải lúc nào cũng có thể xem được trong khung nhìn có thể cuộn hoặc vùng máy khách do quá trình cuộn. Các pixel có thể được xem như biểu diễn kích thước của khu vực đã được cuộn đi sang trái hoặc lên trên cùng.

Các thuộc tính scrollLeftscrollTop là các thuộc tính đọc-ghi, do đó các giá trị của chúng có thể được thao tác.

Lưu ý: Các thuộc tính scrollLeftscrollTop không phải lúc nào cũng trả về số nguyên và có thể trả về các giá trị dấu phẩy động.
Chiều rộng và chiều cao cuộn​
Thuộc tính scrollWidth của một phần tử tính toán clientWidth của phần tử đó cộng với toàn bộ nội dung tràn ở bên trái và bên phải của phần tử đó, trong khi thuộc tính scrollHeight tính toán clientHeight của phần tử đó cộng với toàn bộ nội dung tràn ở mặt trên và mặt dưới của phần tử.

Đây là lý do tại sao nếu một phần tử không có nội dung tràn trên trục x hoặc y của nó, thì các thuộc tính scrollWidthscrollHeight của nó sẽ trả về cùng một giá trị tương ứng với các thuộc tính clientWidthclientHeight của nó.

MDN giải thích các giá trị thuộc tính scrollWidthscrollHeight như sau:
“… Bằng chiều rộng hoặc chiều cao tối thiểu mà phần tử cần để vừa với toàn bộ nội dung trong khung nhìn mà không cần sử dụng thanh cuộn ngang hoặc dọc.”

Hình học cửa sổ và tài liệu​

Giao diện Cửa sổ biểu diễn một cửa sổ chứa tài liệu DOM; thuộc tính document trỏ đến tài liệu DOM được tải trong cửa sổ đó.
Các thuộc tính hình học của tài liệu được tải trong cửa sổ và bản thân cửa sổ có liên quan vì một số lý do. Đôi khi chúng ta cần đọc chiều rộng của toàn bộ khung nhìn và toàn bộ chiều cao của tài liệu, đôi khi chúng ta thậm chí muốn cuộn một trang đến một mức độ nhất định nào đó và vân vân. Vâng, tất nhiên, các thuộc tính để đọc các giá trị và thông tin có liên quan không bị bỏ sót trong CSSOM View Module.

Vì có một phần tử gốc (được gắn nhãn là Document.documentElement trong DOM) định nghĩa toàn bộ tài liệu HTML, chúng ta cũng có thể lấy các thuộc tính chiều cao, chiều rộng và vị trí khác nhau của tài liệu HTML bằng cách truy vấn phần tử gốc.


Chiều rộng và chiều cao cửa sổ​

Các thuộc tính để tính chiều rộng và chiều cao của cửa sổ được chia thành các thuộc tính chiều rộng và chiều cao bên trong và bên ngoài. Để tính chiều rộng và chiều cao bên ngoài của cửa sổ, các thuộc tính chỉ đọc outerWidthouterHeight được sử dụng và chúng lần lượt trả về chiều rộng và chiều cao của toàn bộ cửa sổ trình duyệt.

Để có được chiều rộng và chiều cao bên trong của cửa sổ, các thuộc tính innerWidthinnerHeight được sử dụng. Giá trị được trả về là chiều rộng và chiều cao (bao gồm thanh cuộn) của toàn bộ khung nhìn nơi tài liệu hiển thị.

Bạn có thể cần lấy chiều rộng hoặc chiều cao của khung nhìn bên trong của cửa sổ không có thanh cuộn và đường viền và trong những trường hợp như vậy, hãy sử dụng clientWidth hoặc clientHeight trên Document.documentElement, đây là phần tử gốc biểu diễn cho tài liệu.

Chiều rộng và chiều cao của tài liệu​

Chúng tôi không bao giờ đặt giá trị đường viền, khoảng đệm hoặc lề trên chính phần tử gốc. Tuy nhiên, trên các phần tử có trong Tài liệu, sử dụng các thuộc tính scrollWidthscrollHeight trên phần tử gốc Document.documentElement sẽ trả về toàn bộ chiều rộng và chiều cao của tài liệu.

Giá trị cuộn của cửa sổ và tài liệu​

Cuộn trái và trên cùng​

Như đã khám phá trong phần Hình học nút phần tử, các thuộc tính scrollLeftscrollTop trả về kích thước pixel của vùng cuộn trái hoặc trên cùng của một phần tử.

Do đó, để xác định trạng thái cuộn trái hoặc trên cùng của một tài liệu, sử dụng các thuộc tính scrollLeftscrollTop trên Document.documentElement sẽ trả về các giá trị biểu thị kích thước của phần Tài liệu đã được cuộn đi và không hiển thị trong khung nhìn của cửa sổ.

Các giá trị trạng thái cuộn của một tài liệu cũng có thể được lấy theo cách khác và tốt hơn bằng cách sử dụng các giá trị window.pageXOffsetwindow.pageYOffset.

Các phương pháp cuộn cửa sổ và tài liệu​

Chúng ta có thể cuộn trang theo chương trình để phản hồi một số tương tác của người dùng bằng các phương pháp cuộn được xác định trong Mô-đun chế độ xem CSSOM. Hãy cùng xem xét chúng.

Các phương thức scroll()scrollTo()

Hai phương thức cửa sổ này về cơ bản là cùng một phương thức và cho phép bạn cuộn trang đến các tọa độ cụ thể (x, y) trong Tài liệu. Các giá trị tọa độ biểu thị vị trí tuyệt đối từ góc trên cùng và góc trái của chính tài liệu.

Để trực quan hóa điều này, hãy chạy mã này:
Mã:
window.scrollTo(0, 500);//Cuộn trang theo chiều dọc đến 500 pixel từ gốc trang (0, 0).window.scrollTo(0, 500);//Trang vẫn ở cùng một điểm.
Sau khi chạy window.scrollTo(0, 500) lần đầu tiên, việc chạy lần thứ hai sẽ không có tác dụng gì vì trang đã ở vị trí tuyệt đối cách gốc của Tài liệu 500 pixel trên trục y của nó.

Các phương thức scroll()scrollTo() định nghĩa các tham số xy cho các đối số tương ứng biểu diễn số pixel theo trục ngang và trục dọc tương ứng mà bạn muốn trang được cuộn đến hoặc một từ điển các tùy chọn chứa các giá trị trên cùng, bên trái và hành vi.

Giá trị hành vi xác định cách cuộn xảy ra. Có thể là "smooth", tạo hiệu ứng cuộn mượt mà, hoặc "auto", làm cho việc cuộn giống như một cú nhảy nhanh đến tọa độ đã chỉ định.

Phương pháp scrollBy()

Đây là phương pháp cuộn tương đối. Phương pháp này cuộn trang tương đối với vị trí hiện tại của trang và không liên quan gì đến nguồn gốc Document.

Để xem xét phương pháp này, chúng ta hãy sử dụng ví dụ mã từ phần phương pháp scroll()scrollTo():
Mã:
window.scrollTo(0, 500);//Cuộn trang 500 pixel từ vị trí hiện tại, chẳng hạn như (0, 0), đến (0, 500).window.scrollTo(0, 500);//Cuộn trang thêm 500 pixel từ vị trí hiện tại đến (0, 1000).

Tọa độ​

Hệ thống tọa độ là nguyên nhân gây ra cách xác định vị trí của các phần tử trong các phương thức và thuộc tính của CSSOM View.
Khi chỉ định vị trí của một pixel trong ngữ cảnh đồ họa, vị trí của pixel đó được xác định theo điểm cố định trong ngữ cảnh. Điểm cố định này được gọi là gốc. Vị trí được chỉ định là số pixel lệch khỏi gốc dọc theo mỗi chiều của ngữ cảnh.

CSSOM sử dụng các hệ thống tọa độ chuẩn và nhìn chung chúng chỉ khác nhau về vị trí gốc của chúng.

Tọa độ cửa sổ và tài liệu​

Mặc dù CSSOM sử dụng bốn hệ thống tọa độ chuẩn, nhưng hệ thống tọa độ máy khách và trang được sử dụng nhiều nhất trong Mô-đun CSSOM View. Kích thước hoặc vị trí của các phần tử thường được xác định theo tài liệu hoặc khung nhìn.

Tọa độ máy khách (Tương đối với cửa sổ)​

Tôi không tìm thấy mô tả nào tốt hơn về tọa độ máy khách ngoài mô tả từ MDN:
Hệ tọa độ "máy khách" sử dụng góc trên cùng bên trái của khung nhìn hoặc ngữ cảnh duyệt trong đó sự kiện xảy ra làm gốc. Đây là toàn bộ vùng xem trong đó tài liệu được trình bày. Cuộn không phải là một yếu tố.
Giá trị tọa độ máy khách tương tự như khi sử dụng position: fixed trong CSS và được tính toán từ cạnh trên cùng bên trái của khung nhìn.

Tọa độ trang (Tương đối với tài liệu)​

Hệ tọa độ "trang" cung cấp vị trí của một pixel theo góc trên cùng bên trái của toàn bộ Tài liệu mà pixel đó nằm trong đó. Điều đó có nghĩa là một điểm nhất định trong một phần tử trong tài liệu sẽ giữ nguyên tọa độ trong mô hình trang trừ khi phần tử đó di chuyển (trực tiếp bằng cách thay đổi vị trí của nó hoặc gián tiếp bằng cách thêm hoặc thay đổi kích thước nội dung khác).
Giá trị tọa độ trang tương tự như khi sử dụng position: absolute trong CSS và được tính toán từ cạnh trên bên trái của Document. Vị trí tương đối trên trang của một phần tử sẽ luôn giữ nguyên bất kể cuộn, trong khi vị trí tương đối trên cửa sổ của nó sẽ phụ thuộc vào việc cuộn tài liệu.

Tọa độ phần tử​



Phương thức Element.getBoundingClientRect()

Phương thức này trả về một đối tượng được gọi là đối tượng DOMRect có các thuộc tính là vị trí pixel tương đối với cửa sổ và kích thước của một phần tử. Đây là phương pháp duy nhất bạn cần sử dụng khi cần thao tác một phần tử liên quan đến khung nhìn.

Bạn nên lưu ý rằng trong một số trường hợp, đối tượng DOMRect được trả về không phải lúc nào cũng giữ cùng giá trị thuộc tính hoặc kích thước cho cùng một phần tử. Điều này đặc biệt đúng bất cứ khi nào các phép biến đổi (xoay, xoay, chia tỷ lệ) được thêm vào một phần tử.

Lý do cho điều này khá hợp lý:
Trong trường hợp biến đổi, offsetWidthoffsetHeight trả về chiều rộng và chiều cao bố cục của phần tử, trong khi getBoundingClientRect() trả về chiều rộng và chiều cao kết xuất. Ví dụ, nếu phần tử có width: 100px;transform: scale(0.5); thì getBoundingClientRect() sẽ trả về 50 là chiều rộng, trong khi offsetWidth sẽ trả về 100.
MDN
Bạn có thể hình dung điều này bằng cách nhấp vào nút hiển thị trong bút bên dưới:

Xem Bút [Thuộc tính DOM Rect [phân nhánh]](https://codepen.io/smashingmag/pen/KKedmYx) của Pearl Akpan.
Xem Bút Thuộc tính DOM Rect [phân nhánh] của Pearl Akpan.
Đối tượng được trả về bởi phương thức getBoundingClientRect() chứa sáu thuộc tính kích thước của phần tử mà phương thức được gọi. Các thuộc tính này là:
  • xy trả về tọa độ x và y của gốc phần tử so với cửa sổ;
  • topbottom trả về tọa độ y cho cạnh trên và dưới của hộp phần tử;
  • leftright trả về tọa độ x cho cạnh trái và phải của hộp phần tử;
  • heightwidth trả về toàn bộ chiều rộng và chiều cao của phần tử như thể phần tử được đặt thành box-sizing: border-box.

Tọa độ sự kiện chuột và con trỏ​

Tất cả các đối tượng sự kiện chuột hoặc con trỏ đều có các thuộc tính tọa độ xác định cả tọa độ tương đối với cửa sổ và tương đối với tài liệu nơi xảy ra sự kiện chuột hoặc con trỏ.

Các Tọa độ tương đối theo cửa sổ cho các sự kiện chuột được lưu trữ trong các thuộc tính clientXclientY biểu thị tương ứng các tọa độ xy.

Mặt khác, tọa độ tương đối theo tài liệu cho các sự kiện chuột và con trỏ được lưu trữ trong các thuộc tính pageXpageY của đối tượng sự kiện cho tương ứng các tọa độ xy.

Các trường hợp sử dụng​

Các API trong Mô-đun Chế độ xem CSSOM kết hợp các phương thức và thuộc tính cơ bản nhất nhưng hữu ích nhất để truy cập các thuộc tính hình học của các Phần tử DOM khi được hiển thị trong trình duyệt. Vì các thuộc tính này đang hoạt động nên chúng đáng tin cậy hơn trong các trường hợp cụ thể so với các giá trị CSS của chúng. Nhưng làm thế nào các API này có thể được sử dụng để tạo ra các tính năng giao diện người dùng thực tế?

Chúng tôi sẽ xem xét bốn giải pháp giao diện người dùng hàng ngày được sử dụng trong các trang web và ứng dụng web hiện đại hàng ngày có thể được tạo bằng các API này.

Trong phần này, chúng tôi sẽ chỉ tập trung vào mã JavaScript để triển khai các giải pháp giao diện người dùng này, không phải CSS hay HTML.

Thành phần cuộn lên trên​

Nút cuộn lên đầu cho phép người dùng nhanh chóng quay lại đầu trang mà không tốn nhiều công sức. API CSSOM View cung cấp một phương pháp đơn giản để đạt được điều này với các phương thức scrollTo() và sao chép các phương thức scroll().

Sau đây là cách triển khai nút cuộn lên đầu:

Xem Bút [Cuộn Lên Đầu [phân nhánh]](https://codepen.io/smashingmag/pen/VwdvWwK) của Pearl Akpan.
Xem Bút Cuộn Lên Đầu [forked] của Pearl Akpan.
Để đạt được điều này, chúng ta cần tạo một nút cuộn lên đầu. Trong tệp js của mình, chúng ta thêm trình lắng nghe sự kiện "click" vào nút này:
Mã:
scrollToTop.addEventListener("click", (e) => { window.scrollTo({left: 0, top: 0, behavior: "smooth"});});
Sau đó, chúng ta đăng ký một trình xử lý cho sự kiện này để thực thi (xử lý) những gì xảy ra khi nhấp vào nút này. Mã trong trình xử lý sự kiện gọi phương thức scrollTo() của cửa sổ, với các giá trị xác định đầu trang và hành vi cuộn.

Đối với trải nghiệm người dùng, chắc chắn sẽ không có tác dụng gì khi thấy nút cuộn lên đầu nếu người dùng đã ở đầu trang:
Mã:
document.addEventListener("scroll", (e)=> { if(window.pageYOffset >= 500) { scrollToTop.style.display = "block"; } else { scrollToTop.style.display = "none"; }});
Mã trên chỉ hiển thị nút cuộn lên đầu khi người dùng đã cuộn một khoảng cách bằng cách sử dụng giá trị window.pageYOffset để xác định trang đã được cuộn đến đâu. Nếu trang đã được cuộn lên đến 500 pixel lên trên cùng, thành phần cuộn lên trên sẽ hiển thị và nếu không, thành phần này sẽ vẫn ẩn.

Cuộn vô hạn​

Với việc triển khai trên các phương tiện truyền thông xã hội phổ biến, cuộn vô hạn cho phép người dùng cuộn xuống một trang; nhiều nội dung hơn sẽ tự động và liên tục tải ở cuối trang, giúp người dùng không cần phải nhấp vào trang tiếp theo.

Bạn có đoán được trình duyệt biết cách tải thêm nội dung khi người dùng cuộn xuống trang không? Làm thế nào để xác định khi nào người dùng đã đến cuối trang?

Chúng ta biết rằng document.scrollHeight cung cấp chiều cao tổng thể của một tài liệu, document.clientHeight cung cấp kích thước của màn hình hoặc khung nhìn có thể xem và document.scrollTop hoặc window.pageYOffset cung cấp kích thước của phần tài liệu đã được cuộn lên trên cùng. Chúng ta có thể đoán một cách trực quan rằng nếu document.scrollTop + document.clientHeight >= document.scrollHeight, thì người dùng đã đến cuối trang không? Tôi nghĩ vậy.

Xem Bút [Cuộn vô hạn - [phân nhánh]](https://codepen.io/smashingmag/pen/OJEygVz) của Pearl Akpan.
Xem Bút Cuộn vô hạn - [phân nhánh] của Pearl Akpan.
Cây bút này sử dụng kỹ thuật cuộn vô hạn để nạp thẻ vào trang cho đến khi chúng đạt đến số lượng tối đa. Ở dạng cơ bản nhất, nó mô phỏng cách các trang web thương mại điện tử hiển thị kết quả tìm kiếm cho sản phẩm. Hãy cùng phân tích cách thực hiện điều này.

Chúng tôi sử dụng HTML và CSS để xác định hình thức và kiểu của hộp đựng thẻ và các kiểu mà mỗi phần tử có lớp thẻ phải có. Trong bút của mình, chúng tôi mã hóa cứng bộ thẻ đầu tiên bằng HTML.

Đầu tiên, chúng tôi lấy và gán cho các hằng số những nội dung sau:
  • phần tử hộp đựng thẻ, là phần tử cha cho tất cả các thẻ;
  • phần tử trạng thái hiển thị số lượng thẻ hiện tại đã tải.
Chúng tôi đặt số lượng thẻ tối đa sẽ được tải trên trang và chúng tôi cũng có giá trị tính toán cho số lượng thẻ sẽ được thêm vào trang cho mỗi lần tải. Vì vậy, chúng tôi định nghĩa hằng số totalCardsNocardLoadAmount để lưu trữ các giá trị cho số lượng thẻ tối đa và số lượng thẻ được thêm vào:
Mã:
const cardContainer = document.querySelector("main");const currentCardStats = document.querySelector(".currentCardNo");const cardLoadAmount = 9;const totalCardsNo = 90;let lastIndex;
Chúng ta cần viết một bài kiểm tra để kiểm tra khi nào người dùng ở cuối trang và tải các thẻ. Theo phỏng đoán trước đó của chúng ta, nếu document.scrollTop + document.clientHeight >= document.scrollHeight, thì người dùng của chúng ta ở cuối trang.

Trong bút của chúng ta, chúng ta thêm một trình lắng nghe sự kiện cuộn vào tài liệu của mình và sử dụng một hàm mũi tên để xử lý sự kiện cuộn. Hàm này “xử lý” tất cả các hành động liên quan đến việc tải thẻ nhưng chỉ thực hiện khi người dùng thực sự ở cuối trang đó, tức là điều kiện document.scrollTop + document.clientHeight >= document.scrollHeight trả về true:
Mã:
document.addEventListener("scroll", (e) => { if (document.documentElement.scrollTop + document.documentElement.clientHeight >= document.documentElement.scrollHeight) { const children = cardContainer.children; lastIndex = children.length; }});
Khi người dùng ở cuối trang, chúng ta khởi tạo một hằng số, children, để giữ một HTMLCollection của tất cả các thẻ hiện đang được tải, là các thẻ con hiện tại của cardContainer. Độ dài của children cũng biểu diễn chỉ mục (không phải chỉ mục dạng mảng) của thẻ cuối cùng và chúng tôi lưu trữ giá trị đó trong biến lastIndex.

Trong mã của chúng tôi, chúng tôi sử dụng giá trị của lastIndex để biết có nên tải thêm thẻ hay không hoặc chúng tôi đã đạt đến totalCardsNo sau đó chúng tôi không thể tải thêm thẻ nữa. Nếu giá trị của lastIndex nhỏ hơn totalCardNo, chúng tôi tải thêm thẻ:
Mã:
if(lastIndex < totalCardsNo) { for(let i = 1; i  { animatingElements.forEach((el) => { if(el.getBoundingClientRect().top + 50 < document.documentElement.clientHeight) { el.classList.add("animated"); } else { return; } });});
Bây giờ chúng ta thêm một trình lắng nghe sự kiện cuộn vào tài liệu của mình và đăng ký một trình xử lý kiểm tra xem phần tử có hiển thị 50 pixel trên khung nhìn hay không. Nếu điều kiện trả về true, lớp hoạt hình sẽ được thêm vào phần tử hiển thị một lần và mãi mãi.

Range Slider​

Mặc dù có thể khác nhau về cách triển khai và sử dụng, nhưng range slider là một trong những thành phần web phổ biến nhất. Chúng là các điều khiển đầu vào cho phép người dùng chọn giá trị hoặc thay đổi trạng thái từ một điều khiển hoặc thanh trượt.

Hãy xem kết quả cuối cùng của cây bút này, nơi tôi triển khai một thanh trượt cơ bản:

Xem Bút [Thanh trượt phạm vi [phân nhánh]](https://codepen.io/smashingmag/pen/QWxjgKJ) của Pearl Akpan.
Xem Bút Thanh trượt phạm vi [phân nhánh] của Pearl Akpan.
Chúng tôi sử dụng HTML và CSS để định nghĩa và định kiểu cho các phần tử được chỉ định bằng các lớp .track.thumb tương ứng. Kỹ thuật "kéo" là triển khai chính trong thanh trượt vì ngón tay cái được kéo trong rãnh, điều này xác định một số loại phạm vi.

Vì vậy, chúng tôi đang lấy và gán các phần tử .track.thumb cho các hằng số. Sau đó, chúng tôi khai báo nhưng không khởi tạo các biến draggableshiftX, để sử dụng sau:
Mã:
const thumb = document.querySelector(".thumb");const slider = document.querySelector(".track");let draggable;let x;
Theo trình tự cơ bản nhất, kéo và thả được thực hiện bằng cách:
  1. Di chuyển con trỏ đến đối tượng.
  2. Nhấn và giữ nút trên chuột hoặc thiết bị trỏ khác để "lấy" đối tượng (được định nghĩa là sự kiện "pointerdown").
  3. "Kéo" đối tượng đến vị trí mong muốn bằng cách di chuyển con trỏ đến vị trí đó được định nghĩa là sự kiện "pointermove").
  4. "Thả" đối tượng bằng cách nhả nút được định nghĩa là sự kiện "pointerup").
Mỗi trình tự hành động này đều được định nghĩa trong Sự kiện giao diện người dùng, cụ thể là sự kiện chuột và con trỏ. Bởi vì như chúng ta đã thấy trong phần Tọa độ sự kiện chuột và con trỏ, các sự kiện chuột này có các thuộc tính tọa độ tương đối với tài liệu và tương đối với cửa sổ, và chúng ta sẽ sử dụng chúng để tạo thuật toán kéo và thả cho thanh trượt của mình.

Các sự kiện cần có trình xử lý, vì vậy chúng ta khai báo trình xử lý cho từng sự kiện pointerdown, pointermovepointerup. Trình xử lý đầu tiên mà chúng ta khai báo là hàm prepDrag cho sự kiện pointerdown. Sự kiện pointerdown được kích hoạt bất cứ khi nào chuột hoặc con trỏ được nhấn xuống phần tử có trình lắng nghe sự kiện:
Mã:
function prepDrag(event) { draggable = event.target; x = event.clientX - draggable.getBoundingClientRect().left; document.addEventListener("pointermove", startDrag); document.addEventListener("pointerup", endDrag);}
Vai trò của trình xử lý này là chuẩn bị phần tử để di chuyển. Ví dụ, nếu phần tử được định vị tĩnh, để chuẩn bị cho phần tử cho sự kiện di chuyển hoặc "kéo", trình xử lý prepDrag sẽ phải đặt vị trí của phần tử thành absolute hoặc relative để có thể thao tác vị trí của phần tử thông qua các giá trị top, left của nó.

Khởi tạo draggablex được khai báo toàn cục trong phạm vi cục bộ của trình xử lý prepDrag giúp các trình xử lý khác sẽ được thực thi trong phạm vi đó có thể truy cập các giá trị đó.

Cuối cùng, trong trình xử lý này, chúng ta thêm trình lắng nghe sự kiện pointermovepointerup vào document chứ không phải phần tử thumb. Lý do là sự kiện mousemove thường kích hoạt, nhưng không phải đối với mọi pixel. Do đó, nó có thể gây ra phản hồi kéo và thả không mong muốn. Thêm trình lắng nghe sự kiện vào tài liệu là cách đáng tin cậy hơn để bắt sự kiện mousemove.

Hàm thứ hai, startDrag, xử lý sự kiện pointermove và thực thi tất cả logic xác định cách phần tử thumb di chuyển và vị trí của nó bằng cách thao tác các giá trị kiểu topleft của nó:
Mã:
function startDrag(event) { if (event.clientX < track.offsetLeft || event.clientX > slider.getBoundingClientRect().right){ return; } draggable.style.left = event.clientX - shiftX - track.getBoundingClientRect().left + 'px';}
Chúng tôi muốn giới hạn việc kéo ngón tay cái vào ranh giới của đường dẫn, sao cho ngay cả khi con trỏ di chuyển ra khỏi đường dẫn khi nhấn xuống, ngón tay cái cũng không bị kéo ra ngoài.

Điều này được thực hiện bằng cách thao tác giá trị kiểu left của draggable chỉ khi thuộc tính clientX của sự kiện chuột nằm trong chiều rộng của đường dẫn. Do đó, trong khi con trỏ được nhấn xuống và di chuyển, kiểu vị trí trái của phần tử draggable chỉ thay đổi nếu giá trị clientX của sự kiện chuột không nhỏ hơn giá trị offsetLeft của track cũng không lớn hơn giá trị getBoundingClientRect().right của track.

Hàm cuối cùng, endDrag, xử lý sự kiện pointerup. Nó xóa trình lắng nghe sự kiện pointermovepointerup khỏi tài liệu:
Mã:
function endDrag() { document.removeEventListener("pointermove", startDrag); document.removeEventListener("pointerup", endDrag);}
Vì các sự kiện này được thiết lập để bắt đầu theo một chuỗi liên tục, nên hợp lý khi trình xử lý của chúng không tiếp tục chạy sau khi sự kiện pointerdown (bắt đầu chuỗi) kết thúc:
Mã:
thumb.addEventListener("pointerdown", prepDrag);
Cuối cùng, chúng ta thêm trình lắng nghe sự kiện pointerdown vào phần tử thumb để đăng ký trình xử lý cho sự kiện đầu tiên mà chúng ta lắng nghe.

Kết luận​

Các trường hợp sử dụng được đề cập trong bài viết này chỉ là một phần nhỏ trong những gì có thể đạt được với CSSOM View Module API.

Khi không xem xét đến thao tác DOM nặng nề, tôi tin rằng các phương thức và thuộc tính trong API này cung cấp cho chúng ta rất nhiều công cụ để tùy chỉnh các thuộc tính hình học của các thành phần web để phù hợp với các nhu cầu giao diện khác nhau.
 
Back
Bên trên