Định vị neo

Khi đặt chú thích hoặc trình đơn thả xuống, bạn thường muốn đặt chú thích hoặc trình đơn đó tương ứng với một phần tử khác trên trang. Mặc dù đã có những cách sử dụng tính năng định vị tuyệt đối để đạt được hiệu ứng này, nhưng các yêu cầu phức tạp hơn trước đây thường phải dùng đến việc định vị các mục bằng JavaScript.

Tính năng định vị phần tử neo bằng CSS cung cấp một cách để khai báo vị trí của một phần tử so với một phần tử khác.

Các phần tử chia sẻ Internet

Để biến một phần tử thành một điểm neo, bạn chỉ định cho phần tử đó giá trị anchor-name là bất kỳ chuỗi nào bắt đầu bằng hai dấu gạch ngang. Đây là giá trị nhận dạng mà phần tử được định vị sẽ dùng để tìm điểm neo của phần tử đó và bạn nên đặt cho giá trị nhận dạng này một tên mô tả. Bạn thậm chí có thể đặt nhiều tên điểm neo cho một phần tử nếu phần tử đó sẽ được dùng làm điểm neo theo nhiều cách.

Bạn sẽ cần đặt một số thuộc tính cho phần tử được định vị để có thể liên kết phần tử đó. Trước tiên, bạn cần kéo phần tử ra khỏi luồng của tài liệu để phần tử đó nổi lên bằng cách đặt position: absolute hoặc position: fixed.

Tiếp theo, bạn sẽ cần đặt neo mà bạn muốn liên kết bằng cách đặt position-anchor thành tên neo mà bạn đã đặt trên neo.

Cuối cùng, bạn cần đặt cách định vị điểm neo. Bạn sẽ tìm hiểu thêm về position-area trong phần sau của mô-đun này.

#anchor {
   anchor-name: --my-anchor;
}

#positionedElement {
     position: absolute;
     position-anchor: --my-anchor;
     position-area: end;
}

Chia sẻ Internet ngầm

Popover (cửa sổ bật lên) còn dễ dàng liên kết hơn. Khi bạn mở một cửa sổ bật lên bằng cách sử dụng nút có popovertarget hoặc bằng cách đặt source bằng showPopover({source}), cửa sổ bật lên sẽ có sẵn "neo ngầm". Vì theo mặc định, một cửa sổ bật lên đã nổi với position: fixed, nên để định vị cửa sổ bật lên, bạn chỉ cần đặt vị trí.

#anchor{}

#positionedElement {
  position-area: end;
  margin: unset;
}

Phạm vi của các điểm neo tiềm năng

Bạn có thể triển khai tính năng định vị phần tử neo trong một thành phần để có thể sử dụng một mẫu như trình đơn thả xuống ở nhiều vị trí. Nếu bạn đang sử dụng cùng một anchor-name nhiều lần, làm cách nào để đảm bảo rằng mỗi phần tử được định vị đều tìm thấy điểm neo chính xác?

Các giải pháp JavaScript liên quan đến việc thêm mã nhận dạng riêng biệt vào mỗi điểm neo, sau đó tham chiếu đến mã nhận dạng đó từ phần tử được định vị. Việc này trở nên rườm rà và CSS có một giải pháp đơn giản hơn với anchor-scope.

Thuộc tính anchor-scope đặt tên neo nào sẽ chỉ được so khớp giữa một phần tử và các phần tử con của phần tử đó. Nó chấp nhận danh sách gồm một hoặc nhiều tên điểm neo hoặc từ khoá all để giới hạn phạm vi của tất cả tên điểm neo đã xác định.

Tốt nhất là bạn nên thêm một anchor-scope vào tổ tiên của cả phần tử được định vị và phần tử neo không chứa các phần tử neo khác có cùng tên. Thông thường, điều này nằm trên gốc của thành phần có thể dùng lại.

Ví dụ sau đây cho thấy sự khác biệt mà anchor-scope tạo ra khi được áp dụng cho các phần tử lặp lại có cùng anchor-name. Trong ví dụ này, tất cả các phần tử <img> và biểu ngữ hình ảnh đều tham chiếu đến tên điểm neo --image. Khi anchor-scope được áp dụng cho các phần tử <li>, position-anchor: --image sẽ chỉ khớp với phần tử <img> trong cùng phần tử <li> với biểu ngữ, nếu không, position-anchor: --image sẽ khớp với <img> được kết xuất gần đây nhất.

Khẳng định vị thế

Giờ đây, khi đã liên kết phần tử với điểm neo, bạn có thể định vị phần tử đó. Tính năng định vị điểm neo cung cấp 2 phương thức định vị – position-area và hàm anchor().

position-area

Thuộc tính position-area cho phép bạn đặt một phần tử xung quanh điểm neo bằng cách chỉ định một hoặc hai từ khoá. Điều này bao gồm nhiều trường hợp sử dụng phổ biến và thường là một điểm khởi đầu tốt.

Cách hoạt động của position-area

position-area hoạt động bằng cách tạo một khối chứa mới cho phần tử được định vị trong một vùng do các cạnh của phần tử neo và khối chứa ban đầu của phần tử được định vị tạo ra.

Mặc dù có nhiều từ khoá cho position-area, nhưng bạn có thể chia các từ khoá này thành một số danh mục để dễ hiểu hơn. Anchor-tool.com là một công cụ hữu ích để khám phá cú pháp.

Từ khoá thực

Bạn có thể sử dụng các từ khoá thực tế: top, left, bottom, rightcenter. Ví dụ: position-area: top right sẽ đặt phần tử được định vị ở phía trên và bên phải của phần tử cố định. Các từ khoá này cũng có các từ khoá tương đương về trục vật lý: y-start, x-start, y-endx-end.

Từ khoá logic

Bạn cũng có thể sử dụng các từ khoá logic, block-start, block-end, inline-startinline-end. Ví dụ: position-area: block-end inline-start sẽ đặt phần tử được định vị bên dưới và bên trái điểm neo trong các ngôn ngữ như tiếng Anh, hoặc sau điểm neo trên trục khối và trước điểm neo trên trục nội tuyến trong chế độ viết của tài liệu. Bạn cũng có thể dùng center với một từ khoá logic.

Bạn cũng có thể bỏ qua trục nếu đang chỉ định từ khoá logic, với trục khối ở vị trí đầu tiên và trục nội tuyến ở vị trí thứ hai. position-area: start end giống như position-area: block-start inline-end hoặc thậm chí là position-area: inline-end block-start.

Trải rộng trên nhiều vùng lưới

Cho đến nay, có thể bạn đã nhận thấy rằng những lựa chọn này chỉ cho phép bạn đặt phần tử được định vị trong một không gian lưới duy nhất. Việc thêm tiền tố span vào các thuộc tính thực hoặc logic sẽ thêm khoảng trống lưới trung tâm liền kề. position-area: span-top right sẽ được đặt ở bên phải của phần tử liên kết, và từ dưới cùng của phần tử liên kết đến đầu khối chứa ban đầu của phần tử được đặt.

position-area: block-end span-inline-end là vị trí thường gặp của trình đơn thả xuống.

Từ khoá span-all trải dài trên 3 hàng hoặc cột.

Một từ khoá

Nếu bạn chỉ đặt một từ khoá, thì trục còn lại sẽ được đặt tự động. Tính năng này hoạt động như bạn mong đợi, nhưng bạn nên tìm hiểu cách tính năng này hoạt động.

Nếu từ khoá được cung cấp rõ ràng về trục của nó, thì trục còn lại sẽ được tính là span-all. Điều này có nghĩa là position-area: bottom tương đương với position-area: bottom span-all, và phần tử được định vị sẽ nằm bên dưới phần tử neo, đồng thời có toàn bộ chiều rộng của khối chứa.

Mặt khác, nếu từ khoá không chỉ rõ một trục, thì từ khoá đó sẽ được lặp lại. position-area: start tương đương với start start và được đặt ở trên cùng bên trái của điểm neo trong các ngôn ngữ đọc từ trái sang phải.

Hàm anchor()

Đối với các trường hợp sử dụng nâng cao hơn, position-area có thể không đáp ứng được yêu cầu của bạn. Hàm anchor() cho phép bạn đặt các thuộc tính lồng ghép riêng lẻ dựa trên vị trí của một phần tử khác. Giá trị này được phân giải thành độ dài CSS, tức là bạn có thể sử dụng giá trị này trong các phép tính và với các hàm CSS khác. Ngoài ra, bạn cũng có thể liên kết các cạnh khác nhau với các điểm neo khác nhau.

Hàm anchor() lấy tên và phía của điểm neo. Nếu phần tử của bạn có một neo mặc định (được đặt bằng position-anchor hoặc ngầm, chẳng hạn như với một cửa sổ bật lên), thì bạn có thể bỏ qua tên neo.

.positionedElement {
  block-start: anchor(--my-anchor start);
  /*  OR  */
  position-anchor: --my-anchor;
  block-start: anchor(start);
}

Giá trị dự phòng

Nếu không tìm thấy một điểm neo cho hàm anchor(), thì toàn bộ khai báo sẽ không hợp lệ. Điều này có thể xảy ra nếu phần tử cố định được hiển thị sau phần tử được định vị hoặc nếu không có phần tử nào có anchor-name khớp. Để xử lý vấn đề này, bạn có thể đặt độ dài hoặc tỷ lệ phần trăm dự phòng.

.positionedElement {
   block-start: anchor(--my-anchor, 100px)
}

Trong ví dụ trước, giá trị bên trái của phần tử được định vị được cố định vào --focused-anchor, nhưng anchor-name chỉ tồn tại khi nút đầu tiên được di chuột hoặc lấy tiêu điểm. Vì hàm anchor() phân giải thành một độ dài, nên bạn có thể dùng một neo khác làm phương án dự phòng. Nếu chúng ta không cung cấp một giải pháp dự phòng, thì phần tử được định vị sẽ không được định vị.

Từ khoá phụ

Giá trị phía cố định chọn cạnh nào của điểm cố định để đặt vào. Tương tự như position-area, giá trị phía neo hỗ trợ một số loại cú pháp.

Loại Giá trị Mô tả
Tự nhiên top, left, bottom, right

Từ khoá vật lý tương ứng với một phía cụ thể của phần tử neo, nhưng chỉ có thể dùng trên cùng một trục với phần lồng ghép của phần tử được định vị mà bạn đang đặt.

Ví dụ: top: anchor(bottom) đặt phần trên cùng của phần tử ở dưới cùng của phần tử neo, nhưng left: anchor(top) sẽ không hoạt động.

Bên cạnh inside, outside

Từ khoá inside tương ứng với cùng một phía như thuộc tính lồng ghép và từ khoá outside tương ứng với phía đối diện trên cùng một trục.

Ví dụ: inset-block-start: anchor(inside) đề cập đến phía block-start của phần tử neo và inset-inline-end: (outside) đề cập đến phía inline-start của phần tử neo.

Lôgic start, end, self-start, self-end

Từ khoá logic đề cập đến các cạnh của phần tử neo dựa trên chế độ viết của phần tử được định vị bằng self-startself-end, hoặc bằng chế độ viết của khối chứa phần tử được định vị bằng startend.

Phần trăm 0% – 100%

Giá trị phần trăm đặt phần tử được định vị dọc theo trục từ đầu đến cuối của phần tử neo trên trục đã chỉ định. 0% nằm ở phía start của điểm neo và 100% là phía cuối của điểm neo. center tương đương với 50%. Nếu bạn đang sử dụng tỷ lệ phần trăm trên phần lồng ghép ở phía cuối như bottom, thì tỷ lệ này sẽ không bị đảo ngược – 0% vẫn là phía start của phần tử neo.

Ví dụ này cho thấy cách giá trị phần trăm luôn đi từ đầu đến cuối trên trục đã chỉ định:

Sử dụng anchor()

anchor() là một độ dài nên rất linh hoạt. Bạn có thể thao tác với giá trị bằng các hàm CSS như max()calc().

Một hạn chế là bạn chỉ có thể sử dụng các hàm anchor() trên các thuộc tính lồng ghép.

Ví dụ trước đó sẽ thêm một nền phía sau bảng chi tiết đang mở, nền này sẽ chuyển động mượt mà khi một bảng điều khiển khác mở ra và kéo dài để bao gồm một bảng chi tiết được di chuột vào. Để thực hiện việc này, nó sử dụng min() để chọn chiều dài nhỏ hơn giữa hai điểm neo.

#indicator{
/*  Use the smaller of the 2 values:  */
  inset-block-start: min(
/*   1. The start side of the default anchor, which is the open `<details>` element  */
    anchor(start),
/*   2. The start side of the hovered `<details>` element.    */
    anchor(--hovered start,
/*     If no `<details>` element is hovered, this falls back to infinity px, so that the other value is smaller, and therefore used.   */
       var(calc(1px * infinity)))
  );
}

Ví dụ này cũng sử dụng calc() để thêm khoảng trống nội tuyến xung quanh bảng điều khiển mở.

Sử dụng kích thước của phần tử liên kết

Bạn cũng có thể dùng hàm anchor-size() để sử dụng các phương diện của phần tử cố định cho kích thước, vị trí hoặc lề của phần tử được định vị.

anchor-size() lấy tên của một điểm neo hoặc sử dụng điểm neo mặc định. Theo mặc định, thành phần này sẽ sử dụng kích thước của neo trên trục mà thành phần đang được dùng, vì vậy, width: anchor-size() sẽ trả về chiều rộng của neo. Bạn cũng có thể sử dụng trục còn lại bằng cách chỉ định độ dài bạn muốn, với các từ khoá vật lý widthheight hoặc các từ khoá logic block, inline, self-blockself-inline.

Xử lý nội dung tràn

Bạn đã tạo một thành phần trình đơn thả xuống và sử dụng tính năng định vị phần tử neo để đặt trình đơn thả xuống ở vị trí bạn muốn. Nhưng sau đó, bạn di chuyển trình đơn sang phía bên kia màn hình hoặc sử dụng trình đơn này cho trình đơn người dùng và tên người dùng quá dài. Đột nhiên, trình đơn thả xuống của bạn biến mất khỏi màn hình. Bây giờ bạn phải làm gì?

Tính năng định vị phần tử neo CSS có một hệ thống tích hợp sẵn cho phép bạn nhanh chóng tạo một bộ dự phòng mạnh mẽ khi phần tử được định vị nằm ngoài khối chứa của phần tử đó.

Các lựa chọn dự phòng

Quy tắc position-try-fallbacks sẽ lấy một danh sách các lựa chọn dự phòng. Khi vị trí mặc định tràn, mỗi lựa chọn sẽ được thử theo thứ tự cho đến khi có một vị trí không tràn.

Bạn có thể dùng bất kỳ giá trị position-area nào làm lựa chọn dự phòng. Trong ví dụ này, ở các chế độ viết từ trái sang phải như tiếng Anh, phần tử được định vị sẽ cố gắng được định vị ở cuối phần tử neo, trải dài trên cột ở giữa và cột bên phải. Nếu nội dung đó tràn ra, thì nội dung sẽ cố gắng được đặt ở cuối phần tử liên kết, trải dài trên các cột bên trái và ở giữa. Nếu vị trí đó cũng tràn, thì vị trí sẽ quay trở lại vị trí mặc định, ngay cả khi vị trí đó tràn.

.positioned-element {
  position-area: block-end span-inline-end;
  position-try-fallbacks: block-end span-inline-start;
}

Ngoài ra, còn có một số từ khoá flip- xử lý các trường hợp dự phòng phổ biến. flip-blockflip-inline thử lật phần tử qua các trục khối và trục nội tuyến. Bạn cũng có thể kết hợp các thao tác này với flip-block flip-inline để lật cả hai trục. Giá trị flip-start lật phần tử được định vị qua một đường chéo từ góc bắt đầu đến góc kết thúc của phần tử neo.

Bạn cũng có thể tạo một lựa chọn dự phòng tuỳ chỉnh bằng @position-try. Lựa chọn này cho phép bạn đặt lề, căn chỉnh và thậm chí thay đổi điểm neo.

@position-try --menu-below {
  position-area: bottom span-right;
  margin-top: 1em;
}

#positioned-element {
  position-try: --menu-below;
}

Bạn có thể thêm flip-blockflip-inline vào các lựa chọn dự phòng @position-try để tạo một biến thể.

#positioned-element {
  position-try: --menu-below, flip-inline --menu-below;
}

Trong ví dụ trước, trình duyệt sẽ thực hiện các bước sau, dừng lại ngay khi tìm thấy một giải pháp không bị tràn.

  1. Phần tử được đặt bằng position-area: end, ở dưới cùng bên phải của điểm neo.
  2. Nếu phần tử đó tràn, phần tử sẽ được đặt bằng lựa chọn dự phòng tuỳ chỉnh có tên --bottom-span-right, lựa chọn này sẽ đặt phần tử đó bằng position-area: bottom span-right, có thêm một lề bên dưới.
  3. Nếu nội dung đó tràn ra, phần tử sẽ được đặt bằng flip-inline --bottom-span-right, kết hợp lựa chọn dự phòng tuỳ chỉnh với flip-inline, về cơ bản là position-area: bottom span-left.
  4. Nếu nội dung đó tràn ra, phần tử sẽ được đặt bằng cách sử dụng lựa chọn dự phòng tuỳ chỉnh --use-alternate, lựa chọn này sẽ đặt phần tử bên dưới một điểm neo hoàn toàn khác.
  5. Nếu điều đó vượt quá, phần tử sẽ quay trở lại vị trí ban đầu, với position-area: end, mặc dù vị trí đó được biết là vượt quá.

Thứ tự dự phòng

Theo mặc định, khi vị trí ban đầu tràn, trình duyệt sẽ thử từng lựa chọn trong position-try-fallbacks cho đến khi tìm thấy một vị trí không tràn. Bạn có thể ghi đè hành vi này bằng position-try-order để kiểm thử từng lựa chọn dự phòng và sử dụng lựa chọn có nhiều khoảng trống nhất trên một trục cụ thể.

Bạn có thể chỉ định trục bằng từ khoá logic (most-block-sizemost-inline-size) hoặc bằng từ khoá vật lý (most-heightmost-width).

Bạn có thể kết hợp position-try-orderposition-try-fallbacks với ký hiệu viết tắt position-try, trong đó thứ tự xuất hiện trước.

Thao tác cuộn

Khi người dùng cuộn, họ mong đợi trang sẽ di chuyển mượt mà. Để thực hiện việc này, các trình duyệt có giới hạn về cách sử dụng tính năng định vị phần tử liên kết khi cuộn.

Mặc dù bạn có thể liên kết một phần tử được định vị với các điểm neo trong nhiều vùng chứa có thể cuộn, nhưng phần tử đó sẽ chỉ di chuyển khi một trong các điểm neo cuộn. Đây sẽ là điểm neo mặc định, là điểm neo ngầm định trong một cửa sổ bật lên hoặc giá trị của position-anchor.

Bạn sẽ thấy rằng phần tử được định vị vẫn hiển thị ngay cả khi phần tử neo bị cuộn ra khỏi khung hiển thị. Để ẩn phần tử được định vị khi phần tử neo bị ẩn, hãy đặt position-visibility: anchors-visible. Điều này không chỉ áp dụng khi phần tử liên kết bị cuộn quá mức mà còn áp dụng nếu phần tử đó bị ẩn theo những cách khác, chẳng hạn như bằng visibility: hidden.

Kiểm tra mức độ hiểu biết của bạn

Giá trị nào là giá trị hợp lệ cho mặt trong anchor()?

inside
Chính xác!
25%
Chính xác!
25px
Sai. Mặc dù bạn có thể dùng độ dài như 25px làm giá trị dự phòng, nhưng bạn chỉ có thể dùng tỷ lệ phần trăm cho phía.
block-start
Chưa chính xác
start
Chính xác!

Giá trị nào là giá trị hợp lệ cho position-area?

top
Chính xác!
block-end inline-end
Chính xác!
block-start block-end
Sai. Bạn chỉ có thể xác định một cột hoặc hàng trên mỗi trục.

Những thuộc tính nào hỗ trợ hàm anchor()?

top
Chính xác!
margin-left
Sai.
inset-block-start
Chính xác!
transform
Sai.

Điều gì sẽ xảy ra nếu có nhiều điểm neo có cùng anchor-name?

Phần tử được định vị sẽ được sao chép và liên kết với từng kết quả trùng khớp.
Sai.
Phần tử được định vị sẽ được gắn vào phần tử đầu tiên trong tài liệu.
Sai.
Phần tử được định vị sẽ được liên kết với phần tử cuối cùng trong tài liệu.
Chính xác!
Phần tử được định vị sẽ được gắn vào điểm neo gần nhất.
Sai.