Фокус (и стили) по клику на SVG-элементе

Перевод статьи Click SVG Element to Focus (and Style) с сайта css-tricks.com, опубликовано на css-live.ru, автор — Крис Койер.

Читатель пишет:

Вы не могли бы помочь мне сделать SVG кликабельным с помощью CSS? Задача такая: есть SVG-элемент, и по клику на любую из его частей в этом месте должна появляться обводка? Во вторник у меня собеседование по телефону на работу в качестве SVG-иллюстратора и мне не хотелось бы облажаться.

Допустим, у меня есть карта США в <svg>, и выглядит это так:

<svg class="us" viewBox="0 0 500 400">

  <path d="..." class="wisconsin" />

  <polygon points="..." class="colorado" />

  <g class="michigan">
    <path d="..." class="michigan--up">
    <path d="..." class="michigan--main">
  </g>

  <!-- и т.д.-->

</svg>

Тогда каждый штат — это своего рода прямой потомок <svg>, и его селектором будет svg.us > *

Обычно при слове «кликабельный» я думаю о JavaScript. Вот так можно отслеживать клики для каждого штата. Мы также навесим класс на кликнутый штат.

var allStates = $("svg.us > *");

allStates.on("click", function() {
  
  allStates.removeClass("on");
  $(this).addClass("on");
  
});

Этот класс нужен для оформления. Вы говорили про обводку, так что сделаем её (и заодно покрасим).

.on {
  fill: pink;
  stroke: red;
  stroke-width: 2;
}

Тадам!

See the Pen Click State to Activate by Chris Coyier (@chriscoyier) on CodePen.

Но вы ПРОСИЛИ «кликабельность с помощью CSS«. А это посложнее. Обычно в CSS для этого используется :focus, но не думаю, что есть проверенный способ сделать фокусируемым сам SVG-элемент. Поговаривали (ещё давно) про атрибут focusable, но он так и не появился. Можно взять tabindex, как в HTML, и это вроде бы работает в некоторых браузерах, но рассчитывать на это нельзя. Думаю, лучший способ — это использовать якоря в SVG (серьёзно, их можно использовать и в SVG!), фокусируемые во всех браузерах. Затем применить стили :focus к якорю, которые по каскаду передаются в фигуру.

Амелия Беллами-Ройдс так и поступила в обсуждении на StackOverflow. Вот моя упрощённая версия:

<svg viewBox="0 0 95 50">
  <a xlink:href="#0">
    <circle cx="20" cy="25" r="5" data-Name="shape 1" data-tabindex="0" />
  </a>
  <a xlink:href="#0">
    <circle cx="40" cy="25" r="5" data-Name="shape 2" data-tabindex="0" />
  </a>
  <a xlink:href="#0">
    <circle cx="60" cy="25" r="5" data-Name="shape 3" data-tabindex="0" />
  </a>
  <a xlink:href="#0">
    <circle cx="80" cy="25" r="5" data-Name="shape 4" data-tabindex="0" />
  </a>
</svg>
a:focus {
  fill: pink;
  stroke: red;
  stroke-width: 1;
}

Этого достаточно:

See the Pen SVG with Focusable Elements by Chris Coyier (@chriscoyier) on CodePen.

P.S. Это тоже может быть интересно:

12 Комментарии

  1. Пётр Порошенко

    Спасибо, москали, за перевод.

    1. Владимир Путин

      Пожалуйста, но на украинский теперь сами переводите.

    2. Владимир Путин

      Ну, или на иврит…

  2. Barak Obama

    Good Stuff! Thanks!

  3. Максим

    а как бы сделать так чтоб ещё и текст появлялся?

    1. SelenIT

      Можно так же добавить внутрь элементов a элементы text (типа такого: http://codepen.io/SelenIT/pen/RaPrBo). А дальше можно «химичить» со стилями как угодно, в точности как в HTML.

      1. Максим

        Спасибо, работает. Теперь следующая проблема.
        Надо чтобы блок/кнопка с текстом, которая появляется по клику ещё и увеличивалась (вместе с текстом).
        Сделал увеличение в стилях через transform: scale(1.5);
        Работает, но съезжает т.к. смещаются координаты. Решил сместить через transform: translate(-2%,-2%); Работает, но только по отдельности, как это сделать вместе?
        Такая запись не работает: transform: scale(1.5), translate(-5%,-1%);

        1. SelenIT

          Запятая не нужна: transform: scale(1.5) translate(-5%,-1%);.

          Только учтите, что трансформации комбинируются, справа налево (вторая справа применяется к результату крайней правой, третья справа — к итоговому результату первых двух справа и т.д.). Так что, возможно, будет лучше поменять трансформации местами.

          Но вообще странно, что сдвигается. Может, лучше просто подогнать transform-origin, чтоб на месте оставалась правильная точка?

          1. Максим

            не работает:
            transform: transform-origin(center,center) scale(1.5)
            даже задавал x=»0%» y=»0%»

            1. SelenIT

              transform-origin — отдельное свойство.

  4. Максим

    всё равно пляшет от центра, даже так.
    transform-origin: center center;
    transform: matrix(1.2, 0, 0, 1.2, 0, 0);

    Ума не приложу как задать цетр трансформации для каждого объекта свой или хотя бы для все, но один, например по центру.

  5. Максим

    Проблема решается только конкретным заданием центра через стиль для каждого элемента (в коде SVG) вот так, например style="transform-origin: 20px 25px;".
    универсального способа вывода всех увеличенных элементов в одно место через файл стилей так и не нашел.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.