Quay trở lại những năm 90, hệ điều hành đầu tiên của tôi là Windows. Bây giờ, vào những năm 2020, tôi chủ yếu làm việc để xây dựng các ứng dụng web bằng trình duyệt. Qua nhiều năm, trình duyệt đã chuyển đổi thành một công cụ tuyệt vời và mạnh mẽ hỗ trợ một thế giới rộng lớn các ứng dụng phong phú. Nhiều trong số các ứng dụng này, với giao diện phức tạp và khả năng rộng lớn, thậm chí có thể khiến các chương trình đầu thiên niên kỷ khó khăn nhất cũng phải đỏ mặt.

Các tính năng của trình duyệt gốc như thành phần web đang được các công ty đa quốc gia và các nhà phát triển cá nhân áp dụng và sử dụng trên toàn bộ web.
Trong bài viết này, tôi hy vọng sẽ dạy bạn điều đó bằng cách tái hiện hiệu ứng cửa sổ vỡ mang tính biểu tượng.
Chúng tôi sẽ sử dụng các thành phần web, mô hình thành phần gốc của trình duyệt, để xây dựng giao diện này. Chúng tôi cũng sẽ sử dụng thư viện Lit, giúp đơn giản hóa các API thành phần web gốc.
Nhiều khái niệm tôi nói đến ở đây là những bài học tôi đã học được từ việc xây dựng A2k, một thư viện giao diện người dùng được thiết kế để giúp bạn tạo giao diện người dùng cổ điển với các công cụ hiện đại.
Trong bài viết này, chúng tôi sẽ đề cập đến:
Sau khi StackBlitz hoàn tất thiết lập, bạn sẽ thấy thông tin sau trong cửa sổ trình duyệt:

Lưu ý: Nếu bạn không muốn sử dụng StackBlitz, bạn có thể sao chép kho lưu trữ và chạy hướng dẫn bên trong
Tiếp theo, hãy mở dự án trong trình soạn thảo bạn chọn. Hãy cùng xem nhanh để xem mã khởi động của chúng ta trông như thế nào.
Chúng ta có một tệp HTML rất cơ bản, chỉ nhập một số tệp CSS và JavaScript.
Bạn cũng có thể phát hiện ra một phần tử hoàn toàn mới, phần tử
Các tệp
Tôi đã thêm một số thành phần và chức năng mẫu cho một số thành phần và chức năng, nhưng chúng ta sẽ điền vào các khoảng trống trong suốt bài viết này. Tôi đã nhập tất cả mã của bên thứ nhất và bên thứ ba cần thiết mà chúng ta sẽ sử dụng trong suốt bài viết này.

Chúng ta hãy bắt đầu bằng cách nhảy vào tệp
Chúng ta sẽ cần định nghĩa một lớp mở rộng lớp cơ sở
Một triển khai thực sự cơ bản của một lớp sẽ trông như thế này:
Có hai điều đáng lưu ý:
Bây giờ chúng ta hãy quay lại trình duyệt của mình. Chúng ta nên mong đợi thấy thành phần mới của mình được hiển thị trên trang, nhưng…

Mặc dù thành phần của chúng ta đã được kết xuất, chúng ta vẫn thấy một số nội dung đơn giản chưa được định dạng. Hãy tiếp tục và thêm một số HTML và CSS:
Có một vài điều đáng lưu ý trong đoạn mã trên:
Nếu bạn làm mới, bạn sẽ thấy nội dung sau:

Điều này bắt đầu trông giống thành phần web lấy cảm hứng từ Windows của chúng tôi hơn.
Mẹo chuyên nghiệp: Nếu bạn không thấy trình duyệt, hãy áp dụng những thay đổi mà bạn mong đợi. Mở công cụ phát triển của trình duyệt. Trình duyệt có thể có một số thông báo lỗi hữu ích để giúp bạn tìm ra lỗi ở đâu.
Để thực hiện điều này, chúng ta cần thực hiện ba việc:
Chúng ta sẽ thực hiện điều này bằng cách chỉ định đối tượng
Bước tiếp theo của chúng ta là gán một giá trị mặc định. Chúng ta sẽ thực hiện việc này trong phương thức xây dựng của thành phần.
Lưu ý: Đừng quên gọi
Và cuối cùng, hãy thêm một chút đánh dấu và hiển thị giá trị vào DOM:
Sau khi hoàn tất, hãy quay lại trình duyệt và xem mọi thứ trông như thế nào:

Thật thuyết phục!
Giải lao ngắn
Thật tuyệt khi thấy chúng ta có thể dễ dàng xây dựng giao diện người dùng từ năm 1998 với các nguyên mẫu hiện đại vào năm 2022 như thế nào!

Và chúng ta thậm chí còn chưa đến phần thú vị nữa! Trong các phần tiếp theo, chúng ta sẽ xem xét cách sử dụng một số khái niệm trung gian của Lit để tạo chức năng kéo theo cách có thể tái sử dụng trên các thành phần tùy chỉnh.
Trước khi bắt đầu viết mã, hãy cùng xem qua nhanh các khái niệm mà chúng ta sẽ sử dụng.
Chúng ta có thể trả về các mẫu cụ thể trong một số điều kiện nhất định:
Sẽ có những lúc chúng ta cần thoát khỏi luồng kết xuất thông thường của hệ thống kết xuất Lit. Bạn có thể muốn kết xuất một cái gì đó sau hoặc mở rộng chức năng mẫu của Lit. Điều này có thể đạt được thông qua việc sử dụng chỉ thị. Lit có một số chỉ thị tích hợp sẵn.
Chúng ta sẽ sử dụng chỉ thị
into
Sử dụng
Một cách là xây dựng các thành phần từ nhiều thành phần nhỏ hơn. Ví dụ, một nút biểu tượng trông như thế này:

Đánh dấu có thể có đánh dấu sau:
Trong ví dụ trên, chúng ta đang biên soạn
Một cách khác để biên soạn logic phức tạp là đóng gói trạng thái và hành vi cụ thể vào một lớp. Làm như vậy cho phép chúng ta tách các hành vi cụ thể khỏi mã đánh dấu của mình. Điều này có thể được thực hiện thông qua việc sử dụng bộ điều khiển, một cách liên khung để chia sẻ logic có thể kích hoạt kết xuất lại trong một thành phần. Chúng cũng có lợi ích là móc vào vòng đời của thành phần.
Lưu ý: Vì bộ điều khiển liên khung, chúng có thể được sử dụng trong React và Vue với các bộ điều hợp nhỏ.
Với bộ điều khiển, chúng ta có thể làm một số điều thú vị, như quản lý trạng thái kéo và vị trí của thành phần lưu trữ của nó. Thật thú vị, đó chính xác là những gì chúng tôi dự định làm!
Mặc dù bộ điều khiển có vẻ phức tạp, nhưng nếu chúng ta phân tích bộ khung của nó, chúng ta sẽ có thể hiểu được nó là gì và nó làm gì.
Chúng ta bắt đầu bằng cách khởi tạo bộ điều khiển của mình bằng cách đăng ký nó với thành phần máy chủ và lưu trữ tham chiếu đến máy chủ. Trong trường hợp của chúng tôi, phần tử host sẽ là thành phần
Sau khi thực hiện xong, chúng tôi có thể hook vào các phương thức vòng đời của host, như
Cuối cùng, chúng ta có thể thêm các phương thức và thuộc tính của riêng mình vào bộ điều khiển mà sẽ có sẵn cho thành phần máy chủ của chúng ta. Ở đây, chúng ta đang định nghĩa một vài phương thức riêng tư sẽ được gọi trong các hành động kéo. Chúng ta cũng đang định nghĩa một vài thuộc tính mà phần tử máy chủ của chúng ta có thể truy cập.
Khi các hàm
Nếu điều này nghe có vẻ phức tạp, hy vọng sơ đồ luồng này sẽ trực quan hóa quy trình tốt hơn.

Chúng ta hãy bắt đầu bằng cách hoàn thiện logic khởi tạo của bộ điều khiển:
Sự khác biệt chính giữa đoạn mã này và bộ khung trước đó là việc bổ sung đối số options. Chúng tôi cho phép phần tử máy chủ của mình cung cấp các lệnh gọi lại cho phép chúng tôi truy cập vào hai phần tử khác nhau: phần tử chứa và phần tử có thể kéo. Chúng tôi sẽ sử dụng các phần tử này sau để tính toán các kiểu vị trí chính xác.
Vì những lý do tôi sẽ đề cập sau,
Chúng ta sẽ sử dụng thư viện
Giống như với
Tiếp theo, hãy bắt đầu thêm logic vào các hàm của lớp. Chúng ta sẽ bắt đầu với hàm
Ở đây chúng ta lưu trữ vị trí hiện tại của con trỏ, vị trí này sẽ được sử dụng trong hàm
Khi hàm
Đây chắc chắn không phải là đoạn mã đẹp nhất, vì vậy tôi đã cố gắng hết sức để chú thích đoạn mã để làm rõ những gì đang diễn ra.
Tóm lại:
[*] Sau đó, chúng ta kích hoạt hàm vòng đời cập nhật của máy chủ, khiến phần tử của chúng ta áp dụng các kiểu.
Xem lại hàm nhiều lần để đảm bảo bạn tự tin về chức năng của hàm. Có rất nhiều thứ đang diễn ra, vì vậy đừng lo lắng nếu bạn không hiểu ngay.
Hàm
Chúng ta cũng cần thêm một chút dọn dẹp để đảm bảo rằng chúng ta ngừng theo dõi nếu thành phần của chúng ta vô tình bị ngắt kết nối trong khi đang kéo.
Cuối cùng, chúng ta cần quay lại tệp
Chúng tôi đang sử dụng
Vì chúng ta dự định phân phối các sự kiện từ phần tử kéo của mình, nên chúng ta phải đợi cho đến khi quá trình kết xuất hoàn tất, do đó có câu lệnh
Sau khi hoàn tất mọi việc, bạn sẽ có thể quay lại trình duyệt và kéo thành phần của mình xung quanh, như sau:
(Xem trước lớn)
Và vì chúng ta đã tạo một bộ điều khiển có thể tái sử dụng để xử lý tất cả các chức năng kéo, nên chúng ta có thể thêm hành vi đó vào các thành phần trong tương lai như biểu tượng trên màn hình.
Bây giờ, hãy bắt đầu xây dựng hiệu ứng cửa sổ vỡ tuyệt vời đó khi chúng ta kéo thành phần của mình.
Chúng ta có thể đưa hành vi này vào chính thành phần cửa sổ, nhưng nó không thực sự hữu ích bên ngoài một trường hợp sử dụng cụ thể, tức là tạo ra một hiệu ứng hình ảnh thú vị. Thay vào đó, chúng ta có thể yêu cầu bộ điều khiển kéo của mình phát ra một sự kiện bất cứ khi nào lệnh gọi lại
Để tạo hiệu ứng cửa sổ bị hỏng, chúng ta cần thực hiện hai việc:
Chúng ta đang định nghĩa hàm mà chúng ta muốn kích hoạt khi nhấp vào nút và truyền nó đến phần tử sẽ được gọi khi nhấp. Đây là một tùy chọn hoàn toàn khả thi và là cách tiếp cận mà tôi sẽ sử dụng nếu phần tử và hàm gọi lại nằm gần nhau.
Như tôi đã đề cập trước đó, chúng ta sẽ không đưa hành vi cửa sổ bị hỏng vào thành phần, vì việc truyền xuống trình xử lý sự kiện thông qua một số thành phần web khác nhau sẽ trở nên cồng kềnh. Thay vào đó, chúng ta có thể tận dụng đối tượng sự kiện cửa sổ gốc để có một thành phần phân phối sự kiện và bất kỳ thành phần tổ tiên nào của nó lắng nghe và phản hồi. Hãy xem ví dụ sau:
Lưu ý: Đừng quên xem qua các tài nguyên MDN nếu bạn cần ôn lại về Sự kiện DOM gốc.
Chúng ta có hai thành phần, một trình lắng nghe và một trình phân phối. Trình lắng nghe là một thành phần thêm trình lắng nghe sự kiện vào chính nó. Nó lắng nghe sự kiện
Thành phần thứ hai của chúng ta,
Chúng ta hãy tiếp tục và phân phối các sự kiện từ bộ điều khiển kéo của mình. Chúng ta sẽ muốn tạo một phiên bản của
Sau đó, chúng ta sẽ tạo tùy chọn
Tiếp tục và thử triển khai logic này bên trong hàm
Gợi ý: Chúng ta sẽ muốn phân phối sự kiện từ phần tử kéo của mình. Đừng quên rằng chúng ta đã lưu tham chiếu đến phần tử trên phiên bản của bộ điều khiển.
Trước khi tôi tiếp tục và tiết lộ câu trả lời, hãy thiết lập trình lắng nghe của chúng ta. Theo cách đó, chúng ta có thể xác định xem chúng ta đã kết nối đúng trình phân phối sự kiện hay chưa.
Nhảy vào tệp
Bây giờ bạn có thể nhảy vào trình duyệt, kéo phần tử của mình và xem nhật ký trong bảng điều khiển.
Bạn có thể kiểm tra giải pháp của mình với giải pháp của tôi bên dưới:
Tuyệt! Việc duy nhất còn lại cần làm là thêm phần tử cửa sổ bị hỏng vào DOM mỗi khi chúng ta nhận được sự kiện kéo.
Chúng ta sẽ cần tạo một thành phần cửa sổ bị hỏng mới trông giống như sau:

Cửa sổ bị hỏng của chúng ta sẽ trông đẹp hơn một chút so với cửa sổ thông thường không có nội dung. Đánh dấu cho thành phần sẽ rất đơn giản. Chúng ta sẽ có các
Sau khi bạn tạo thành phần, chúng ta có thể kiểm tra xem nó có hoạt động chính xác không bằng cách thêm nội dung sau vào tệp
Nếu bạn thấy nội dung sau trong trình duyệt của mình, thì xin chúc mừng! Cửa sổ bị hỏng của bạn đang hoạt động hoàn hảo.

Tôi sẽ không tiết lộ câu trả lời ở đây, nhưng nếu bạn muốn thử, tài liệu hướng dẫn của Lit sẽ giúp ích nếu bạn gặp khó khăn.
Để tạo hiệu ứng cửa sổ bị hỏng, chúng ta chỉ cần thực hiện một số thao tác sau:
Chúng ta đang lắng nghe sự kiện
Đoạn mã trên thực hiện hai việc:
Ở đây, chúng ta bắt buộc phải tạo phần tử cửa sổ bị hỏng và áp dụng các kiểu của mình. Đối với bất kỳ ai quen thuộc với việc viết HTML bằng JavaScript (hoặc thậm chí là jQuery), thì đây không phải là một khái niệm xa lạ. Bây giờ, chúng ta sẽ thêm thành phần của mình vào DOM.
Chúng ta cần phải rất cụ thể về nơi chúng ta muốn đặt phần tử. Chúng ta không thể chỉ thêm nó vào phần thân; nếu không, nó sẽ che mất phần tử cửa sổ chính của chúng ta.

Chúng ta cũng không thể viết nó là phần tử đầu tiên của
Một giải pháp là thêm thành phần của chúng ta vào DOM ngay trước phần tử chứa của chúng ta. Tất cả các nhà phát triển JavaScript ngoài kia có thể háo hức viết tập lệnh riêng của họ để quản lý điều này nhưng may mắn thay, cửa sổ có chức năng hoàn hảo dành cho chúng ta:
Hàm trên rất tiện dụng cho phép chúng ta kiểm soát vị trí thêm phần tử. Tập lệnh này chèn phần tử mới của chúng ta trước phần tử chứa.
Tập lệnh hoàn thiện của chúng ta trông như thế này:
Quay lại trình duyệt và bắt đầu kéo cửa sổ của bạn. Bây giờ bạn sẽ thấy hiệu ứng cửa sổ tuyệt vời của mình!
Nếu tập lệnh của bạn không hoạt động, thì đừng lo lắng! Mở bảng điều khiển của bạn và xem bạn có thể gỡ lỗi sự cố không. Bạn thậm chí có thể chạy qua các đoạn mã ở trên và đảm bảo mọi thứ đã được sao chép chính xác.
Nhưng Microsoft đã làm điều này cách đây 20 năm. Tôi rất muốn xem cộng đồng Smashing sáng tạo có thể tạo ra những hiệu ứng tuyệt vời nào thay thế! Đây là tôi đang có một chút vui vẻ:
(Xem trước lớn)
Vui lòng gửi cho Twitter của tôi những gì bạn đã tạo ra bằng bài viết này.
Cửa sổ có thể kéo là một phần của thư viện giao diện người dùng thành phần web của tôi, A2k, mà bạn có thể sử dụng trong các dự án của riêng bạn. Bạn có thể thử bằng cách truy cập kho GitHub.
Nếu bạn muốn hỗ trợ dự án, bạn có thể theo dõi tôi trên Twitter để cập nhật hoặc để lại kho lưu trữ là một ngôi sao GitHub.
Tôi cũng muốn gửi lời cảm ơn đến Elliott Marquez, Nhà phát triển Lit tại Google, vì đã là người đánh giá kỹ thuật.

Các tính năng của trình duyệt gốc như thành phần web đang được các công ty đa quốc gia và các nhà phát triển cá nhân áp dụng và sử dụng trên toàn bộ web.
Vậy thì tại sao không nắm bắt công nghệ hiện tại bằng cách tôn vinh các giao diện của quá khứ?Trong trường hợp bạn đang tự hỏi liệu có ai đang sử dụng Thành phần web không:
- GitHub
- YouTube
- Twitter (tweet được nhúng)
- SalesForce
- ING
- Ứng dụng web Photoshop
- Chrome devtools
- Giao diện người dùng Firefox hoàn chỉnh
- Ứng dụng web Apple Music
— Danny Moerkerke (@dannymoerkerke) 5 tháng 8, 2022
Trong bài viết này, tôi hy vọng sẽ dạy bạn điều đó bằng cách tái hiện hiệu ứng cửa sổ vỡ mang tính biểu tượng.

Chúng tôi sẽ sử dụng các thành phần web, mô hình thành phần gốc của trình duyệt, để xây dựng giao diện này. Chúng tôi cũng sẽ sử dụng thư viện Lit, giúp đơn giản hóa các API thành phần web gốc.
Nhiều khái niệm tôi nói đến ở đây là những bài học tôi đã học được từ việc xây dựng A2k, một thư viện giao diện người dùng được thiết kế để giúp bạn tạo giao diện người dùng cổ điển với các công cụ hiện đại.
Trong bài viết này, chúng tôi sẽ đề cập đến:
- những điều cơ bản về việc tạo thành phần web bằng Lit;
- cách tùy chỉnh dễ dàng hành vi của thành phần bằng các công cụ tích hợp của Lit;
- cách đóng gói chức năng có thể tái sử dụng;
- cách phân phối và phản hồi các sự kiện bằng các phương pháp luồng dữ liệu nâng cao.
Bắt đầu Đã bắt đầu
Bạn có thể theo dõi cho phép trong trình duyệt bằng cách sử dụng StackBlitz.Sau khi StackBlitz hoàn tất thiết lập, bạn sẽ thấy thông tin sau trong cửa sổ trình duyệt:

Lưu ý: Nếu bạn không muốn sử dụng StackBlitz, bạn có thể sao chép kho lưu trữ và chạy hướng dẫn bên trong
README.md
. Bạn cũng có thể sử dụng tệp Lit VSCode để tô sáng cú pháp và các tính năng.Tiếp theo, hãy mở dự án trong trình soạn thảo bạn chọn. Hãy cùng xem nhanh để xem mã khởi động của chúng ta trông như thế nào.
index.html
Chúng ta có một tệp HTML rất cơ bản, chỉ nhập một số tệp CSS và JavaScript.Bạn cũng có thể phát hiện ra một phần tử hoàn toàn mới, phần tử
a2k-window
. Bạn sẽ chưa từng thấy phần tử này trước đây vì đây là phần tử tùy chỉnh mà chúng ta sẽ tự xây dựng. Vì chúng ta chưa tạo và đăng ký thành phần này nên trình duyệt sẽ chuyển sang hiển thị nội dung HTML bên trong.Các tệp .js
khác nhau
Tôi đã thêm một số thành phần và chức năng mẫu cho một số thành phần và chức năng, nhưng chúng ta sẽ điền vào các khoảng trống trong suốt bài viết này. Tôi đã nhập tất cả mã của bên thứ nhất và bên thứ ba cần thiết mà chúng ta sẽ sử dụng trong suốt bài viết này.Phần thưởng: Phông chữ
Tôi cũng đã thêm một số phông chữ cổ điển để giải trí! Đây là một phông chữ tuyệt vời lấy cảm hứng từ MS-2000 do Lou tạo ra. Bạn có thể tải xuống và sử dụng trong các dự án của riêng bạn nếu bạn muốn thêm một chút hương vị thiên niên kỷ vào các thiết kế của mình.Phần 1: Xây dựng Thành phần Web đầu tiên của chúng ta
Viết Đánh dấu của chúng ta
Điều đầu tiên chúng ta muốn làm là tạo một thành phần cửa sổ trông thật thuyết phục. Chỉ với một vài dòng mã, chúng ta sẽ có được thành phần sau.
Chúng ta hãy bắt đầu bằng cách nhảy vào tệp
a2k-window.js
của chúng ta. Chúng ta sẽ viết một đoạn mã mẫu nhỏ để đưa thành phần của mình vào hoạt động.Chúng ta sẽ cần định nghĩa một lớp mở rộng lớp cơ sở
LitElement
của Lit. Bằng cách mở rộng từ LitElement
, lớp của chúng ta có khả năng quản lý các trạng thái và thuộc tính phản ứng. Chúng ta cũng cần triển khai hàm render
trên lớp trả về mã đánh dấu để render.Một triển khai thực sự cơ bản của một lớp sẽ trông như thế này:
Mã:
class A2kWindow extends LitElement { render() { return html` `; }}
- Chúng ta có thể chỉ định ID phần tử sau đó được đóng gói trong thành phần web. Giống như tài liệu cấp cao nhất, không được phép có ID trùng lặp trong cùng một thành phần, nhưng các thành phần web khác hoặc các thành phần DOM bên ngoài có thể sử dụng cùng một ID.
- Phần tử
slot
là một công cụ tiện dụng có thể hiển thị mã đánh dấu tùy chỉnh được truyền xuống từ phần tử cha. Đối với những ai quen thuộc với React, chúng ta có thể ví nó như một cổng thông tin React hiển thị nơi bạn đặt propchildren
. Bạn có thể làm nhiều hơn thế nữa với nó, nhưng điều đó nằm ngoài phạm vi của bài viết này.
a2k-window
. Bên dưới lớp thành phần của chúng ta, hãy viết mã sau:
Mã:
customElements.define("a2k-window", A2kWindow);

Mặc dù thành phần của chúng ta đã được kết xuất, chúng ta vẫn thấy một số nội dung đơn giản chưa được định dạng. Hãy tiếp tục và thêm một số HTML và CSS:
Mã:
class A2kWindow extends LitElement { static styles = css` :host { font-family: var(--font-primary); } #window { width: min(80ch, 100%); } #panel { border: var(--border-width) solid var(--color-gray-400); box-shadow: 2px 2px var(--color-black); background-color: var(--color-gray-500); } #draggable { background: linear-gradient(90deg, var(--color-blue-100) 0%, var(--color-blue-700) 100% ); user-select: none; } #draggable p { font-weight: bold; margin: 0; color: white; padding: 2px 8px; } [data-dragging="idle"] { cursor: grab; } [data-dragging="dragging"] { cursor: grabbing; } `; render() { return html` `; }}
- Chúng tôi định nghĩa các kiểu được giới hạn trong phần tử tùy chỉnh này thông qua thuộc tính
static styles
. Do cách đóng gói kiểu hoạt động, thành phần của chúng tôi sẽ không bị ảnh hưởng bởi bất kỳ kiểu bên ngoài nào. Tuy nhiên, chúng tôi có thể sử dụng các biến CSS mà chúng tôi đã thêm vàostyles.css
để áp dụng các kiểu từ nguồn bên ngoài. - Tôi đã thêm một số kiểu cho các phần tử DOM hiện chưa có, nhưng chúng tôi sẽ sớm thêm chúng.
Nếu bạn làm mới, bạn sẽ thấy nội dung sau:

Điều này bắt đầu trông giống thành phần web lấy cảm hứng từ Windows của chúng tôi hơn.

Mẹo chuyên nghiệp: Nếu bạn không thấy trình duyệt, hãy áp dụng những thay đổi mà bạn mong đợi. Mở công cụ phát triển của trình duyệt. Trình duyệt có thể có một số thông báo lỗi hữu ích để giúp bạn tìm ra lỗi ở đâu.
Làm cho thành phần web của chúng ta có thể tùy chỉnh
Bước tiếp theo của chúng ta là tạo tiêu đề cho thành phần cửa sổ. Một tính năng cốt lõi của các thành phần web là thuộc tính phần tử HTML. Thay vì mã hóa cứng nội dung văn bản của tiêu đề cửa sổ, chúng ta có thể biến nó thành đầu vào thuộc tính trên phần tử. Chúng ta có thể sử dụng Lit để làm cho các thuộc tính của mình phản ứng, kích hoạt các phương thức vòng đời khi thay đổi.Để thực hiện điều này, chúng ta cần thực hiện ba việc:
- Xác định các thuộc tính phản ứng,
- Gán một giá trị mặc định,
- Kết xuất giá trị của thuộc tính phản ứng vào DOM.
Mã:
class A2kWindow extends LitElement { static styles = css`...`; static properties = { heading: {}, }; render() {...}}
properties
tĩnh trên lớp của mình. Sau đó, chúng ta chỉ định tên của các thuộc tính mà chúng ta muốn, cùng với một số tùy chọn được truyền qua dưới dạng đối tượng. Các tùy chọn mặc định của Lit xử lý chuyển đổi thuộc tính chuỗi theo mặc định. Điều này có nghĩa là chúng ta không cần áp dụng bất kỳ tùy chọn nào và có thể để heading
làm đối tượng trống.Bước tiếp theo của chúng ta là gán một giá trị mặc định. Chúng ta sẽ thực hiện việc này trong phương thức xây dựng của thành phần.
Mã:
class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() { super(); this.heading = "Building Retro Web Components with Lit"; } render() {...}}
super()
!Và cuối cùng, hãy thêm một chút đánh dấu và hiển thị giá trị vào DOM:
Mã:
class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() {...} render() { trả về html`
${this.heading}
`; }}

Thật thuyết phục!

Phần thưởng
Áp dụng tiêu đề tùy chỉnh choa2k-element
từ tệp index.html
.Giải lao ngắn 
Thật tuyệt khi thấy chúng ta có thể dễ dàng xây dựng giao diện người dùng từ năm 1998 với các nguyên mẫu hiện đại vào năm 2022 như thế nào!
Và chúng ta thậm chí còn chưa đến phần thú vị nữa! Trong các phần tiếp theo, chúng ta sẽ xem xét cách sử dụng một số khái niệm trung gian của Lit để tạo chức năng kéo theo cách có thể tái sử dụng trên các thành phần tùy chỉnh.
Phần 2: Làm cho thành phần của chúng ta có thể kéo được
Đây là lúc mọi thứ trở nên hơi khó khăn! Chúng ta đang chuyển sang một số vùng Lit trung gian, vì vậy đừng lo lắng nếu mọi thứ không hoàn toàn hợp lý.Trước khi bắt đầu viết mã, hãy cùng xem qua nhanh các khái niệm mà chúng ta sẽ sử dụng.
Directives
Như bạn đã thấy, khi viết các mẫu HTML của chúng ta trong Lit, chúng ta viết chúng bên trong thẻ literalshtml
. Điều này cho phép chúng ta sử dụng JavaScript để thay đổi hành vi của các mẫu. Chúng ta có thể làm những việc như đánh giá biểu thức:
Mã:
html`
${this.heading}
`
Mã:
html`
${this.heading ? this.heading : “Vui lòng nhập tiêu đề”}
`
Chúng ta sẽ sử dụng chỉ thị
styleMap
, cho phép chúng ta áp dụng các kiểu trực tiếp cho một phần tử thông qua một đối tượng JavaScript. Sau đó, đối tượng được chuyển đổi thành các kiểu nội tuyến của phần tử. Điều này sẽ hữu ích khi chúng ta điều chỉnh vị trí của phần tử cửa sổ vì vị trí của phần tử được quản lý bởi các thuộc tính CSS. Tóm lại, styleMap
chuyển thành:
Mã:
const top = this.top // một biến mà chúng ta có thể lấy từ lớp, hàm hoặc bất kỳ nơi nàostyleMap({ position: "absolute", left: "100px", top})
Mã:
"position: absolute; top: 50px; left: 100px;"
styleMap
giúp bạn dễ dàng sử dụng các biến để thay đổi kiểu.Bộ điều khiển
Lit có một số cách tiện dụng để tạo thành các thành phần phức tạp từ các đoạn mã nhỏ hơn, có thể tái sử dụng.Một cách là xây dựng các thành phần từ nhiều thành phần nhỏ hơn. Ví dụ, một nút biểu tượng trông như thế này:

Đánh dấu có thể có đánh dấu sau:
Mã:
class IconButton extends LitElement { render() { return html` ` }}
IconButton
của mình từ hai thành phần web đã tồn tại từ trước.Một cách khác để biên soạn logic phức tạp là đóng gói trạng thái và hành vi cụ thể vào một lớp. Làm như vậy cho phép chúng ta tách các hành vi cụ thể khỏi mã đánh dấu của mình. Điều này có thể được thực hiện thông qua việc sử dụng bộ điều khiển, một cách liên khung để chia sẻ logic có thể kích hoạt kết xuất lại trong một thành phần. Chúng cũng có lợi ích là móc vào vòng đời của thành phần.
Lưu ý: Vì bộ điều khiển liên khung, chúng có thể được sử dụng trong React và Vue với các bộ điều hợp nhỏ.
Với bộ điều khiển, chúng ta có thể làm một số điều thú vị, như quản lý trạng thái kéo và vị trí của thành phần lưu trữ của nó. Thật thú vị, đó chính xác là những gì chúng tôi dự định làm!
Mặc dù bộ điều khiển có vẻ phức tạp, nhưng nếu chúng ta phân tích bộ khung của nó, chúng ta sẽ có thể hiểu được nó là gì và nó làm gì.
Mã:
export class DragController { x = 0; y = 0; state = "idle" styles = {...} constructor(host, options) { this.host = host; this.host.addController(this); } hostDisconnected() {...} onDragStart = (pointer, ev) => {...}; onDrag = (_, pointers) => {...};}
a2k-window
của chúng tôi.Sau khi thực hiện xong, chúng tôi có thể hook vào các phương thức vòng đời của host, như
hostConnected
, hostUpdate
, hostUpdated
, hostDisconnected
, v.v., để chạy logic kéo cụ thể. Trong trường hợp của chúng tôi, chúng tôi chỉ cần hook vào hostDisconnected
cho mục đích dọn dẹp.Cuối cùng, chúng ta có thể thêm các phương thức và thuộc tính của riêng mình vào bộ điều khiển mà sẽ có sẵn cho thành phần máy chủ của chúng ta. Ở đây, chúng ta đang định nghĩa một vài phương thức riêng tư sẽ được gọi trong các hành động kéo. Chúng ta cũng đang định nghĩa một vài thuộc tính mà phần tử máy chủ của chúng ta có thể truy cập.
Khi các hàm
onDrag
và onDragStart
được gọi, chúng ta cập nhật thuộc tính styles
của mình và yêu cầu thành phần máy chủ của chúng ta kết xuất lại. Vì thành phần lưu trữ của chúng ta chuyển đổi đối tượng kiểu này thành CSS nội tuyến (thông qua chỉ thị styleMap
), nên thành phần của chúng ta sẽ áp dụng các kiểu mới.Nếu điều này nghe có vẻ phức tạp, hy vọng sơ đồ luồng này sẽ trực quan hóa quy trình tốt hơn.

Viết bộ điều khiển của chúng ta
Có thể nói đây là phần kỹ thuật nhất của bài viết, chúng ta hãy kết nối bộ điều khiển của mình!Chúng ta hãy bắt đầu bằng cách hoàn thiện logic khởi tạo của bộ điều khiển:
Mã:
export class DragController { x = 0; y = 0; state = "idle"; styles = { position: "absolute", top: "0px", left: "0px", }; constructor(host, options) { const { getContainerEl = () => null, getDraggableEl = () => Promise.resolve(null), } = options; this.host = host; this.host.addController(this); this.getContainerEl = getContainerEl; getDraggableEl().then((el) => { if (!el) return; this.draggableEl = el; this.init(); }); } init() {...} hostDisconnected() {...} onDragStart = (pointer) => {...}; onDrag = (_, pointers) => {...};}
Vì những lý do tôi sẽ đề cập sau,
getDraggableEl
là một lời hứa trả về phần tử có thể kéo. Sau khi lời hứa được giải quyết, chúng ta lưu trữ phần tử trên phiên bản bộ điều khiển và chúng ta sẽ khởi chạy hàm khởi tạo, hàm này sẽ gắn trình lắng nghe sự kiện kéo vào phần tử có thể kéo.
Mã:
init() { this.pointerTracker = new PointerTracker(this.draggableEl, { start: (...args) => { this.onDragStart(...args); this.state = "dragging"; this.host.requestUpdate(); return true; }, move: (...args) => { this.onDrag(...args); }, end: (...args) => { this.state = "idle"; this.host.requestUpdate(); }, });}
PointerTracker
để theo dõi các sự kiện con trỏ một cách dễ dàng. Sử dụng thư viện này dễ chịu hơn nhiều so với việc viết logic chế độ nhập liệu chéo, đa trình duyệt để hỗ trợ các sự kiện con trỏ.PointerTracker
yêu cầu hai đối số, draggableEl
và một đối tượng hàm hoạt động như trình xử lý sự kiện cho các sự kiện kéo:-
start
: được gọi khi con trỏ được nhấn xuốngdraggableEl
; -
move
: được gọi khi kéodraggableEl
xung quanh; -
end
: được gọi khi chúng ta nhả con trỏ khỏidraggableEl
.
trạng thái
kéo, gọi hàm gọi lại của bộ điều khiển hoặc cả hai. Phần tử máy chủ của chúng ta sẽ sử dụng thuộc tính state
làm thuộc tính phần tử, vì vậy chúng ta kích hoạt this.host.requestUpdate
để đảm bảo máy chủ được hiển thị lại.Giống như với
draggableEl
, chúng ta gán tham chiếu đến thể hiện pointerTracker
cho bộ điều khiển của chúng ta để sử dụng sau.Tiếp theo, hãy bắt đầu thêm logic vào các hàm của lớp. Chúng ta sẽ bắt đầu với hàm
onDragStart
:
Mã:
onDragStart = (pointer, ev) => { this.cursorPositionX = Math.floor(pointer.pageX); this.cursorPositionY = Math.floor(pointer.pageY);};
onDrag
.
Mã:
onDrag = (_, pointers) => { this.calculateWindowPosition(pointers[0]);};
onDrag
được gọi, nó sẽ cung cấp danh sách các con trỏ đang hoạt động. Vì chúng ta sẽ chỉ phục vụ cho một cửa sổ được kéo tại một thời điểm, nên chúng ta có thể chỉ cần truy cập mục đầu tiên trong mảng một cách an toàn. Sau đó, chúng ta sẽ gửi mục đó đến một hàm xác định vị trí mới của phần tử. Hãy thắt dây an toàn vì nó hơi hoang dã:
Mã:
calculateWindowPosition(pointer) { const el = this.draggableEl; const containerEl = this.getContainerEl(); if (!el || !containerEl) return; const oldX = this.x; const oldY = this.y; //Các số thực của JavaScript có thể kỳ lạ, vì vậy chúng ta sẽ chuyển chúng thành số nguyên. const parsedTop = Math.floor(pointer.pageX); const parsedLeft = Math.floor(pointer.pageY); //Các số thực của JavaScript có thể kỳ lạ, vì vậy chúng ta sẽ chuyển chúng thành số nguyên. const cursorPositionX = Math.floor(pointer.pageX); const cursorPositionY = Math.floor(pointer.pageY); const hasCursorMoved = cursorPositionX !== this.cursorPositionX || cursorPositionY !== this.cursorPositionY; // Chúng ta chỉ cần tính toán vị trí cửa sổ nếu vị trí con trỏ đã thay đổi. if (hasCursorMoved) { const { bottom, height } = el.getBoundingClientRect(); const { right, width } = containerEl.getBoundingClientRect(); // Sự khác biệt giữa vị trí trước đó của con trỏ và vị trí hiện tại của nó. const xDelta = cursorPositionX - this.cursorPositionX; const yDelta = cursorPositionY - this.cursorPositionY; // Con đường hạnh phúc - nếu phần tử không cố gắng vượt ra ngoài ranh giới của trình duyệt. this.x = oldX + xDelta; this.y = oldY + yDelta; const outOfBoundsTop = this.y < 0; const outOfBoundsLeft = this.x < 0; const outOfBoundsBottom = bottom + yDelta > window.innerHeight; const outOfBoundsRight = right + xDelta >= window.innerWidth; const isOutOfBounds = outOfBoundsBottom || outOfBoundsLeft || outOfBoundsRight || outOfBoundsTop; // Đặt vị trí con trỏ cho lần tiếp theo hàm này được gọi. this.cursorPositionX = cursorPositionX; this.cursorPositionY = cursorPositionY; // Nếu không, chúng ta buộc cửa sổ phải nằm trong cửa sổ trình duyệt. if (outOfBoundsTop) { this.y = 0; } else if (outOfBoundsLeft) { this.x = 0; } else if (outOfBoundsBottom) { this.y = window.innerHeight - height; } else if (outOfBoundsRight) { this.x = Math.floor(window.innerWidth - width); } this.updateElPosition(); // Chúng ta kích hoạt bản cập nhật vòng đời. this.host.requestUpdate(); }}updateElPosition(x, y) { this.styles.transform = `translate(${this.x}px, ${this.y}px)`;}
Tóm lại:
- Khi hàm được gọi, chúng ta kiểm tra xem cả
draggableEl
vàcontainerEl
đều khả dụng. - Sau đó, chúng ta truy cập vị trí của phần tử và vị trí của con trỏ.
- Sau đó, chúng ta tính toán xem con trỏ có di chuyển hay không. Nếu không, chúng ta không làm gì cả.
- Chúng ta đặt vị trí
x
vày
mới cho phần tử. - Chúng ta xác định xem phần tử có cố gắng phá vỡ ranh giới của cửa sổ hay không.Nếu có, chúng ta sẽ cập nhật vị trí
x
hoặcy
để đưa phần tử trở lại trong ranh giới của cửa sổ.
this.styles
bằng các giá trị x
và y
mới.[*] Sau đó, chúng ta kích hoạt hàm vòng đời cập nhật của máy chủ, khiến phần tử của chúng ta áp dụng các kiểu.
Xem lại hàm nhiều lần để đảm bảo bạn tự tin về chức năng của hàm. Có rất nhiều thứ đang diễn ra, vì vậy đừng lo lắng nếu bạn không hiểu ngay.
Hàm
updateElPosition
là một trợ giúp nhỏ trong lớp để áp dụng các kiểu cho thuộc tính styles
.Chúng ta cũng cần thêm một chút dọn dẹp để đảm bảo rằng chúng ta ngừng theo dõi nếu thành phần của chúng ta vô tình bị ngắt kết nối trong khi đang kéo.
Mã:
hostDisconnected() { if (this.pointerTracker) { this.pointerTracker.stop(); }}
a2k-window.js
và thực hiện ba việc:- khởi tạo bộ điều khiển,
- áp dụng kiểu vị trí,
- theo dõi trạng thái kéo.
Mã:
class A2kWindow extends LitElement { static styles = css`...`; static properties = {...}; constructor() {...} drag = new DragController(this, { getContainerEl: () => this.shadowRoot.querySelector("#window"), getDraggableEl: () => this.getDraggableEl(), }); async getDraggableEl() { await this.updateComplete; return this.shadowRoot.querySelector("#draggable"); } render() { return html`
${this.heading}
`; }}
this.shadowRoot.querySelector(selector)
để truy vấn shadow DOM của chúng tôi. Điều này cho phép bộ điều khiển của chúng tôi truy cập các phần tử DOM qua ranh giới shadow DOM.Vì chúng ta dự định phân phối các sự kiện từ phần tử kéo của mình, nên chúng ta phải đợi cho đến khi quá trình kết xuất hoàn tất, do đó có câu lệnh
await this.updateComplete
.Sau khi hoàn tất mọi việc, bạn sẽ có thể quay lại trình duyệt và kéo thành phần của mình xung quanh, như sau:

(Xem trước lớn)
Phần 3: Tạo hiệu ứng Cửa sổ vỡ
Thành phần của chúng ta khá độc lập, điều này thật tuyệt. Chúng ta có thể sử dụng thành phần cửa sổ này ở bất kỳ đâu trên trang web của mình và kéo nó mà không cần viết thêm bất kỳ mã nào.Và vì chúng ta đã tạo một bộ điều khiển có thể tái sử dụng để xử lý tất cả các chức năng kéo, nên chúng ta có thể thêm hành vi đó vào các thành phần trong tương lai như biểu tượng trên màn hình.
Bây giờ, hãy bắt đầu xây dựng hiệu ứng cửa sổ vỡ tuyệt vời đó khi chúng ta kéo thành phần của mình.
Chúng ta có thể đưa hành vi này vào chính thành phần cửa sổ, nhưng nó không thực sự hữu ích bên ngoài một trường hợp sử dụng cụ thể, tức là tạo ra một hiệu ứng hình ảnh thú vị. Thay vào đó, chúng ta có thể yêu cầu bộ điều khiển kéo của mình phát ra một sự kiện bất cứ khi nào lệnh gọi lại
onDrag
được gọi. Điều này có nghĩa là bất kỳ ai sử dụng thành phần của chúng tôi đều có thể lắng nghe sự kiện kéo và làm bất cứ điều gì họ muốn.Để tạo hiệu ứng cửa sổ bị hỏng, chúng ta cần thực hiện hai việc:
- gửi và lắng nghe sự kiện kéo;
- thêm phần tử cửa sổ bị hỏng vào DOM.
Gửi và lắng nghe sự kiện trong Lit
Lit có một số cách khác nhau để xử lý sự kiện. Bạn có thể thêm trình lắng nghe sự kiện trực tiếp trong các mẫu của mình, như sau:
Mã:
handleClick() { console.log("Clicked");}render() { html`Click me!`}
Như tôi đã đề cập trước đó, chúng ta sẽ không đưa hành vi cửa sổ bị hỏng vào thành phần, vì việc truyền xuống trình xử lý sự kiện thông qua một số thành phần web khác nhau sẽ trở nên cồng kềnh. Thay vào đó, chúng ta có thể tận dụng đối tượng sự kiện cửa sổ gốc để có một thành phần phân phối sự kiện và bất kỳ thành phần tổ tiên nào của nó lắng nghe và phản hồi. Hãy xem ví dụ sau:
Mã:
// Trình lắng nghe sự kiệnclass SpecialListener mở rộng LitElement { constructor() { super() this.specialLevel = ''; this.addEventListener('special-click', this.handleSpecialClick) } handleSpecialClick(e) { this.specialLevel = e.detail.specialLevel; } render() { html`
${this.specialLevel}
` }}// Bộ điều phối sự kiệnlớp SpecialButton mở rộng LitElement { handleClick() { const event = new CustomEvent("special-click", { bubbles: true, combined: true, detail: { specialLevel: 'high', }, }); this.dispatchEvent(event); } render() { html`Click me!` }}
Chúng ta có hai thành phần, một trình lắng nghe và một trình phân phối. Trình lắng nghe là một thành phần thêm trình lắng nghe sự kiện vào chính nó. Nó lắng nghe sự kiện
special-click
và đưa ra giá trị mà sự kiện gửi qua.Thành phần thứ hai của chúng ta,
SpecialButton
, là hậu duệ của SpecialListener
. Đây là một thành phần phân phối sự kiện khi nhấp. Mã bên trong phương thức handleClick
rất thú vị, vì vậy chúng ta hãy cùng tìm hiểu những gì đang diễn ra ở đây:- Chúng ta tạo một đối tượng sự kiện bằng cách tạo một thể hiện của
CustomEvent
. - Đối số đầu tiên của
CustomEvent
là tên của sự kiện mà chúng ta muốn phân phối. Trong trường hợp của chúng ta, đó làspecial-click
. - Đối số thứ hai của
CustomEvent
là đối số tùy chọn. Ở đây chúng ta thiết lập ba tùy chọn:bubbles
,composed
vàdetail
. - Đặt
bubbles
thành true cho phép sự kiện của chúng ta chảy lên cây DOM đến các thành phần tổ tiên. - Đặt
composed
thành true cho phép sự kiện của chúng ta lan truyền ra ngoài gốc shadow của phần tử. - Cuối cùng, chúng ta phân phối sự kiện của mình bằng cách kích hoạt
this.dispatchEvent(event)
.
handleSpecialClick
.Chúng ta hãy tiếp tục và phân phối các sự kiện từ bộ điều khiển kéo của mình. Chúng ta sẽ muốn tạo một phiên bản của
CustomEvent
với tên sự kiện là window-drag
. Chúng ta sẽ muốn đặt tùy chọn composed
và bubbles
thành true.Sau đó, chúng ta sẽ tạo tùy chọn
detail
với một thuộc tính duy nhất: containerEl
. Cuối cùng, chúng ta sẽ muốn phân phối sự kiện.Tiếp tục và thử triển khai logic này bên trong hàm
onDrag
.Gợi ý: Chúng ta sẽ muốn phân phối sự kiện từ phần tử kéo của mình. Đừng quên rằng chúng ta đã lưu tham chiếu đến phần tử trên phiên bản của bộ điều khiển.
Trước khi tôi tiếp tục và tiết lộ câu trả lời, hãy thiết lập trình lắng nghe của chúng ta. Theo cách đó, chúng ta có thể xác định xem chúng ta đã kết nối đúng trình phân phối sự kiện hay chưa.
Nhảy vào tệp
script.js
và thêm các dòng sau:
Mã:
function onWindowDrag() { console.log('dragging');}window.addEventListener('window-drag', onWindowDrag);
Bạn có thể kiểm tra giải pháp của mình với giải pháp của tôi bên dưới:
Mã:
onDrag = (_, pointers) => { this.calculateWindowPosition(pointers[0]); const event = new CustomEvent("window-drag", { bubbles: true, compose: true, detail: { containerEl: this.getContainerEl(), }, }); this.draggableEl.dispatchEvent(event);};
Chúng ta sẽ cần tạo một thành phần cửa sổ bị hỏng mới trông giống như sau:

Cửa sổ bị hỏng của chúng ta sẽ trông đẹp hơn một chút so với cửa sổ thông thường không có nội dung. Đánh dấu cho thành phần sẽ rất đơn giản. Chúng ta sẽ có các
div
lồng nhau, mỗi div chịu trách nhiệm cho các khía cạnh khác nhau của phần tử:- div ngoài cùng sẽ chịu trách nhiệm về vị trí.
- div ở giữa sẽ chịu trách nhiệm về giao diện.
- div trong cùng sẽ chịu trách nhiệm về chiều rộng và chiều cao.
Mã:
export class BrokenWindow extends LitElement { static properties = { height: {}, width: {}, top: {}, left: {}, }; static styles = css` #outer-container { position: absolute; display: flex; } #middle-container { border: var(--border-width) solid var(--color-gray-400); box-shadow: 2px 2px var(--color-black); background-color: var(--color-gray-500); } `; render() { return html` `; }}window.customElements.define("a2k-broken-window", BrokenWindow);
index.html
của chúng ta:
Mã:

Phần thưởng
Bạn có thể nhận thấy rằng cả thành phầna2k-window
và thành phần a2k-broken-window
của chúng tôi đều có nhiều kiểu giống nhau. Chúng ta có thể tận dụng một trong các kỹ thuật sáng tác của Lit để trừu tượng hóa các đánh dấu và kiểu lặp lại thành một thành phần riêng biệt, a2k-panel
. Sau khi hoàn tất, chúng ta có thể sử dụng lại a2k-panel
trong các thành phần cửa sổ của mình.Tôi sẽ không tiết lộ câu trả lời ở đây, nhưng nếu bạn muốn thử, tài liệu hướng dẫn của Lit sẽ giúp ích nếu bạn gặp khó khăn.
Kết xuất cửa sổ bị hỏng khi kéo
Chúng ta đang ở điểm dừng cuối cùng trong hành trình thành phần web cổ điển của mình.Để tạo hiệu ứng cửa sổ bị hỏng, chúng ta chỉ cần thực hiện một số thao tác sau:
- Lắng nghe sự kiện
window-drag
; - Truy cập vào các kiểu của vùng chứa;
- Tạo phần tử
a2k-broken-window
mới; - Đặt
top
,left
,height
,width
thuộc tính cho phần tử mới của chúng ta; - Chèn cửa sổ bị hỏng vào DOM.
script.js
của chúng ta:
Mã:
function onWindowDrag(e) { ...}window.addEventListener("window-drag", onWindowDrag);
window-drag
và thiết lập lệnh gọi lại để nhận đối tượng sự kiện khi được gọi.
Mã:
function onWindowDrag(e) { const { containerEl } = e.detail; const { width, top, left, height } = containerEl.getBoundingClientRect();}window.addEventListener("window-drag", onWindowDrag);
- Truy cập
containerEl
từ đối tượng chi tiết. - Sau đó, chúng ta sử dụng hàm
getBoundingClientRect
củacontainerEl
để lấy các thuộc tính CSS của phần tử.
Mã:
function onWindowDrag(e) { const { containerEl } = e.detail; const { width, top, left, height } = containerEl.getBoundingClientRect(); const newEl = document.createElement("a2k-broken-window"); newEl.setAttribute("width", width); newEl.setAttribute("top", top); newEl.setAttribute("left", left); newEl.setAttribute("height", height);}
Chúng ta cần phải rất cụ thể về nơi chúng ta muốn đặt phần tử. Chúng ta không thể chỉ thêm nó vào phần thân; nếu không, nó sẽ che mất phần tử cửa sổ chính của chúng ta.

Chúng ta cũng không thể viết nó là phần tử đầu tiên của
body
; nếu không, cửa sổ cũ nhất sẽ xuất hiện phía trên các cửa sổ mới hơn.Một giải pháp là thêm thành phần của chúng ta vào DOM ngay trước phần tử chứa của chúng ta. Tất cả các nhà phát triển JavaScript ngoài kia có thể háo hức viết tập lệnh riêng của họ để quản lý điều này nhưng may mắn thay, cửa sổ có chức năng hoàn hảo dành cho chúng ta:
Mã:
containerEl.insertAdjacentElement("beforebegin", newEl);
Tập lệnh hoàn thiện của chúng ta trông như thế này:
Mã:
function onWindowDrag(e) { const { containerEl } = e.detail; const { width, top, left, height } = containerEl.getBoundingClientRect(); const newEl = document.createElement("a2k-broken-window"); newEl.setAttribute("width", width); newEl.setAttribute("top", top); newEl.setAttribute("left", left); newEl.setAttribute("height", height); containerEl.insertAdjacentElement("beforebegin", newEl);}window.addEventListener("window-kéo", onWindowDrag);
Nếu tập lệnh của bạn không hoạt động, thì đừng lo lắng! Mở bảng điều khiển của bạn và xem bạn có thể gỡ lỗi sự cố không. Bạn thậm chí có thể chạy qua các đoạn mã ở trên và đảm bảo mọi thứ đã được sao chép chính xác.
Phần thưởng
Chúng tôi đã tạo ra một hiệu ứng có thể kéo tuyệt vời bằng cách lắng nghe các sự kiện kéo và viết một số logic tùy chỉnh bên trong trình xử lý.Nhưng Microsoft đã làm điều này cách đây 20 năm. Tôi rất muốn xem cộng đồng Smashing sáng tạo có thể tạo ra những hiệu ứng tuyệt vời nào thay thế! Đây là tôi đang có một chút vui vẻ:

(Xem trước lớn)
Vui lòng gửi cho Twitter của tôi những gì bạn đã tạo ra bằng bài viết này.

Kết luận
Cảm ơn bạn đã theo dõi hết bài viết! Chúng ta đã đề cập đến nhiều vấn đề. Tôi hy vọng bài viết này đã giúp bạn thoải mái hơn khi viết các thành phần web bằng thư viện Lit tuyệt vời. Quan trọng nhất, tôi hy vọng bạn thích tham gia cùng tôi để xây dựng một cái gì đó thú vị.Cửa sổ có thể kéo là một phần của thư viện giao diện người dùng thành phần web của tôi, A2k, mà bạn có thể sử dụng trong các dự án của riêng bạn. Bạn có thể thử bằng cách truy cập kho GitHub.
Nếu bạn muốn hỗ trợ dự án, bạn có thể theo dõi tôi trên Twitter để cập nhật hoặc để lại kho lưu trữ là một ngôi sao GitHub.
Tôi cũng muốn gửi lời cảm ơn đến Elliott Marquez, Nhà phát triển Lit tại Google, vì đã là người đánh giá kỹ thuật.