Расположение якоря

При размещении подсказки или раскрывающегося меню часто требуется позиционировать их относительно другого элемента на странице. Хотя для достижения этого эффекта существовали способы использования абсолютного позиционирования, для более сложных задач традиционно приходилось прибегать к позиционированию элементов с помощью JavaScript.

Позиционирование якорей CSS позволяет декларативно позиционировать элемент относительно другого элемента.

Элементы привязки

Чтобы сделать элемент якорем, ему присваивается значение anchor-name представляющее собой любую строку, начинающуюся с двух дефисов. Это идентификатор, который позиционируемый элемент будет использовать для поиска своего якоря, и полезно дать ему описательное имя. Вы даже можете присвоить элементу несколько имён якорей, если он будет использоваться в качестве якоря разными способами.

Вам потребуется задать несколько свойств позиционируемого элемента, чтобы его можно было привязать. Во-первых, нужно вывести элемент из потока документа, сделав его плавающим, установив position: absolute или position: fixed .

Далее вам нужно будет указать, к какому якорю вы хотите привязаться, установив в качестве значения position-anchor заданное вами для якоря имя якоря.

Наконец, вам нужно будет указать, как расположить якорь. Подробнее о position-area вы узнаете далее в этом модуле.

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

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

Неявные привязки

Привязать поповеры ещё проще. Когда вы открываете поповер с помощью кнопки с popovertarget или устанавливая source с помощью showPopover({source}) , у поповера уже установлен «неявный якорь». Поскольку поповер уже плавающий и по умолчанию имеет фиксированное position: fixed , для его позиционирования достаточно просто задать позицию.

#anchor{}

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

Определение потенциальных якорей

Вы можете реализовать позиционирование якоря как часть компонента, чтобы использовать шаблон, например, раскрывающееся меню, в нескольких местах. Если вы используете одно и то же anchor-name несколько раз, как убедиться, что каждый позиционируемый элемент находит правильный якорь?

Решения JavaScript предполагают добавление уникальных идентификаторов к каждому якорю и последующую ссылку на них из позиционируемого элемента. Это довольно громоздко, и в CSS есть более простое решение — свойство anchor-scope .

Свойство anchor-scope определяет, какие имена якорей будут сопоставляться только между элементом и его потомками. Оно принимает список из одного или нескольких имён якорей или ключевое слово all чтобы ограничить область действия всех определённых имён якорей.

anchor-scope в идеале следует добавлять к предку как позиционируемого элемента, так и якорного элемента, который не содержит других якорных элементов с тем же именем. Часто это происходит в корне повторно используемого компонента.

В следующем примере показано, как свойство anchor-scope применяется к повторяющимся элементам с одинаковым anchor-name . В этом примере все элементы <img> и баннеры изображений ссылаются на имя якоря --image . При применении anchor-scope к элементам <li> свойство position-anchor: --image будет соответствовать только элементу <img> внутри того же элемента <li> , что и баннер, в противном случае оно будет соответствовать последнему отображённому элементу <img> .

Позиционирование

Теперь, когда вы привязали элемент к якорю, пора его позиционировать. Для позиционирования якоря предусмотрены два метода: position-area и функция anchor() .

position-area

Свойство position-area позволяет позиционировать элемент вокруг якоря, указав одно или два ключевых слова. Это охватывает множество распространённых случаев использования и часто является хорошей отправной точкой.

Как работает position-area

position-area работает, создавая новый содержащий блок для позиционируемого элемента в области, образованной краями якоря и исходного содержащего блока позиционируемого элемента.

Хотя для position-area доступно множество ключевых слов, их можно разбить на несколько категорий для более легкого понимания. Anchor-tool.com — отличный инструмент для изучения синтаксиса.

Физические ключевые слова

Вы можете использовать физические ключевые слова top , left , bottom , right и center . Например, position-area: top right разместит позиционируемый элемент выше и правее якоря. У этих ключевых слов также есть физические эквиваленты по осям: y-start , x-start , y-end и x-end .

Логические ключевые слова

Вы также можете использовать логические ключевые слова: block-start , block-end , inline-start и inline-end . Например, position-area: block-end inline-start разместит позиционируемый элемент под и слева от анкера в таких языках, как английский, или после анкера на оси блока и перед анкером на оси строки в режиме написания документа. center также можно использовать с логическим ключевым словом.

Вы также можете опустить ось, если указываете логические ключевые слова, указав сначала блочную ось, а затем встроенную. position-area: start end — это то же самое, что position-area: block-start inline-end или даже position-area: inline-end block-start .

Охват нескольких областей сетки

До сих пор вы, возможно, заметили, что эти параметры позволяют размещать позиционируемый элемент только в пределах одного пространства сетки. Добавление префикса span к физическим или логическим свойствам добавляет смежное центральное пространство сетки. position-area: span-top right будет позиционироваться справа от якоря и от нижней части якоря до верхней части исходного содержащего блока позиционируемого элемента.

Распространенной областью позиционирования для раскрывающегося меню является position-area: block-end span-inline-end .

Ключевое слово span-all охватывает 3 строки или столбца.

Одно ключевое слово

Если вы задаёте только одно ключевое слово, вторая ось задаётся автоматически. В целом, всё работает так, как и ожидалось, но может быть полезно понять, как это работает.

Если указанное ключевое слово однозначно определяет ось, другая ось вычисляется как span-all . Это означает, что position-area: bottom эквивалентно position-area: bottom span-all , и позиционируемый элемент будет находиться под якорем и будет иметь доступ ко всей ширине содержащего его блока.

С другой стороны, если ключевое слово явно не указывает на ось, оно повторяется. position-area: start эквивалентно start start и размещается в левом верхнем углу якоря в языках с письмом слева направо.

Функция anchor()

Для более сложных случаев использования position-area может не соответствовать вашим требованиям. Функция anchor() позволяет задавать индивидуальные свойства вставки в зависимости от положения другого элемента. Это преобразуется в длину CSS , что позволяет использовать её в вычислениях и с другими функциями CSS. Кроме того, вы также можете привязывать разные стороны к разным якорям.

Функция anchor() принимает имя и сторону якоря. Если у вашего элемента есть якорь по умолчанию, заданный с помощью position-anchor или неявно, например, с помощью всплывающего окна, вы можете опустить имя якоря.

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

Резервные значения

Если для функции anchor() не удаётся найти якорь, всё объявление будет недействительным. Это может произойти, если якорь отображается после позиционируемого элемента или если нет элемента с совпадающим anchor-name . Для решения этой проблемы можно задать резервную длину или процент.

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

В предыдущем примере значение left позиционируемого элемента привязано к --focused-anchor , но это anchor-name существует только при наведении курсора или фокусе на первой кнопке. Поскольку функция anchor() преобразуется в длину, можно использовать другой якорь в качестве резервного варианта. Если бы мы не предоставили резервный вариант, позиционируемый элемент не был бы спозиционирован.

Ключевые слова на якорной стороне

Значение стороны якоря определяет, к какому краю якоря будет прикрепляться якорь. Подобно position-area , значение стороны якоря поддерживает несколько различных типов синтаксиса.

Тип Ценности Описание
Физический top , left , bottom , right

Физические ключевые слова соответствуют определенной стороне якоря, но могут использоваться только на той же оси, что и вставка позиционируемого элемента, которую вы устанавливаете.

Например, top: anchor(bottom) размещает верхнюю часть элемента по низу якоря, но left: anchor(top) не будет работать.

Сторона inside , outside

Ключевое слово inside соответствует той же стороне, что и свойство inset, а ключевое слово outside соответствует противоположной стороне на той же оси.

Например, inset-block-start: anchor(inside) относится к стороне block-start якоря, а inset-inline-end: (outside) относится к стороне inline-start якоря.

Логический start , end , self-start , self-end

Логические ключевые слова ссылаются на стороны якоря на основе режима записи позиционируемого элемента с self-start и self-end или на основе режима записи содержащего блока позиционируемого элемента с start и end .

Процент 0% - 100%

Процентное значение размещает позиционируемый элемент вдоль оси от начала до конца якоря на указанной оси. 0% соответствует start стороне якоря, а 100% — конечной стороне якоря. Значение center эквивалентно 50% . Если вы используете процентное значение для отступа с конечной стороны, например, bottom , это не меняет местами — 0% по-прежнему соответствует start стороне якоря.

В этом примере показано, как процентное значение всегда идет от начала до конца по указанной оси:

Использование anchor()

Поскольку anchor() — это длина, он очень гибкий. Вы можете управлять значением с помощью CSS-функций, таких как max() и calc() .

Одним из ограничений является то, что функции anchor() можно использовать только для свойств-вставок.

В предыдущем примере фон за открытой панелью сведений добавляется плавно, анимируется при открытии другой панели и растягивается, включая в себя наведённую на неё панель сведений. Для этого используется min() который выбирает наименьшее расстояние между двумя якорями.

#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)))
  );
}

В примере также используется calc() для добавления внутреннего пространства вокруг открытой панели.

Используя размер якоря

Вы также можете использовать функцию anchor-size() чтобы использовать размеры якоря для размера, положения или поля позиционируемого элемента.

anchor-size() принимает имя якоря или использует якорь по умолчанию. По умолчанию используется размер якоря по оси, по которой он используется, поэтому width: anchor-size() вернет ширину якоря. Вы также можете использовать другую ось, указав нужную длину с помощью физических ключевых слов width и height или логических ключевых слов block , inline , self-block и self-inline .

Обработка переполнения

Вы создали компонент раскрывающегося меню и использовали позиционирование якорей, чтобы разместить его там, где нужно. Но затем вы перемещаете меню на другую сторону экрана или используете его для пользовательского меню, и имя пользователя становится слишком длинным. Внезапно ваш раскрывающийся список исчезает с экрана. Что теперь?

Позиционирование якорей CSS имеет встроенную систему, которая позволяет быстро создать надежный набор резервных вариантов, когда позиционируемый элемент оказывается за пределами содержащего его блока.

Запасные варианты

Правило position-try-fallbacks принимает список резервных вариантов. При переполнении позиции по умолчанию каждый вариант будет перебран по порядку, пока не найдётся позиция, не вызывающая переполнения.

В качестве резервного варианта можно использовать любое значение position-area . В этом примере, в режимах письма слева направо, например, в английском языке, позиционируемый элемент будет пытаться разместиться внизу якоря, охватывая центральный и правый столбцы. Если это значение выходит за пределы, он будет пытаться разместиться внизу якоря, охватывая левый и центральный столбцы. Если и это значение выходит за пределы, позиция вернется к положению по умолчанию, даже если оно выходит за пределы.

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

Существует также несколько ключевых слов flip- , которые обрабатывают распространённые резервные случаи. flip-block и flip-inline пытаются перевернуть элемент относительно блочной и строчной осей. Их также можно комбинировать с flip-block flip-inline для переворота относительно обеих осей. Значение flip-start переворачивает позиционируемый элемент относительно диагональной линии от начального до конечного угла якоря.

Вы также можете создать собственный резервный вариант с помощью @position-try — это позволит вам задать поля, выравнивание и даже изменить привязку.

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

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

Для создания варианта к резервным опциям @position-try можно добавить flip-block и flip-inline .

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

В предыдущем примере браузер выполняет следующие шаги и останавливается, как только находит решение, не приводящее к переполнению.

  1. Элемент размещается с position-area: end , в правом нижнем углу якоря.
  2. Если это переполняется, элемент размещается с пользовательской резервной опцией с именем --bottom-span-right , которая размещает его с position-area: bottom span-right , с дополнительным полем снизу.
  3. Если это переполняется, элемент помещается с flip-inline --bottom-span-right , который объединяет пользовательский резервный параметр с flip-inline , который по сути является position-area: bottom span-left .
  4. Если это значение выходит за пределы указанного диапазона, элемент размещается с использованием резервной опции --use-alternate , которая помещает его под совершенно другой якорь.
  5. Если произойдет переполнение, элемент возвращается в исходное положение с position-area: end , даже если известно, что это приведет к переполнению.

Резервный порядок

По умолчанию при переполнении исходной позиции браузер пробует все варианты из position-try-fallbacks пока не найдётся вариант, не вызывающий переполнения. Вы можете переопределить это поведение с помощью position-try-order чтобы протестировать каждый вариант резервного варианта и использовать тот, который занимает больше всего места по указанной оси.

Ось можно указать либо с помощью логических ключевых слов most-block-size и most-inline-size , либо с помощью физических ключевых слов most-height и most-width .

position-try-order и position-try-fallbacks можно комбинировать с помощью сокращенной записи position-try , при этом порядок должен идти первым.

Прокрутка

При прокрутке страницы пользователь ожидает, что страница будет двигаться плавно. Чтобы добиться этого, браузеры ограничивают использование якорных точек при прокрутке.

Хотя позиционируемый элемент можно привязать к якорям в разных контейнерах прокрутки, он будет перемещаться только в ответ на прокрутку одного из якорей. Это будет якорь по умолчанию, который может быть либо неявным якорем из всплывающего окна, либо значением position-anchor .

Обратите внимание, что позиционируемый элемент остаётся видимым, даже если якорь прокручивается. Чтобы скрыть позиционируемый элемент, когда якорь скрыт, установите position-visibility: anchors-visible . Это относится не только к ситуации, когда якорь находится за пределами прокрутки, но и к другим способам его скрытия, например, с помощью visibility: hidden .

Проверьте свое понимание

Каковы допустимые значения для стороны в anchor() ?

inside
Правильный!
25%
Правильный!
25px
Неверно. В качестве резервного значения можно использовать длину, например, 25px , но для стороны можно использовать только проценты.
block-start
Неверно
start
Правильный!

Каковы допустимые значения для position-area ?

top
Правильный!
block-end inline-end
Правильный!
block-start block-end
Неверно. На каждой оси можно определить только один столбец или строку.

Какие свойства поддерживают функцию anchor() ?

top
Правильный!
margin-left
Неверно.
inset-block-start
Правильный!
transform
Неверно.

Что произойдет, если есть несколько якорей с одинаковым anchor-name ?

Позиционированный элемент дублируется и привязывается к каждому совпадению.
Неверно.
Позиционируемый элемент привязывается к первому в документе.
Неверно.
Позиционируемый элемент привязывается к последнему в документе.
Правильный!
Позиционируемый элемент привязывается к ближайшему якорю.
Неверно.