Подтвердить что ты не робот

Можно ли использовать HTML.querySelector() для выбора по атрибуту xlink в SVG?

Дано:

<body>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <a xlink:href="url"></a>
    </svg>
</body>

Можно ли использовать HTML DOM .querySelector() или .querySelectorAll(), чтобы выбрать ссылку внутри SVG по содержимому своего атрибута xlink:href?

Это работает:

document.querySelector('a')                    // <a xlink:href="url"/>

Это не так:

document.querySelector('[href="url"]')         // null
document.querySelector('[xlink:href="url"]')   // Error: not a valid selector
document.querySelector('[xlink\:href="url"]')  // Error: not a valid selector
document.querySelector('[xlink\\:href="url"]') // null

Есть ли способ записи этого селектора атрибутов, чтобы он "видел" xlink:href?

4b9b3361

Ответ 1

Селектор запросов может обрабатывать пространства имен, но он становится сложным, потому что

  • Синтаксис для указания пространств имен в селекторах CSS отличается от html;

  • API-интерфейс querySelector не имеет способа назначить префикс пространства имен (например, xlink) для реального пространства имен (например, "http://www.w3.org/1999/xlink").

В первой точке соответствующая часть спецификаций CSS позволяет указать не пространство имен (по умолчанию), определенное пространство имен или любое пространство имен:

@namespace foo "http://www.example.com";
[foo|att=val] { color: blue }
[*|att] { color: yellow }
[|att] { color: green }
[att] { color: green }

Первое правило будет соответствовать только элементам с атрибутом att в пространстве имен http://www.example.com со значением "val".

Второе правило будет соответствовать только элементам с атрибутом att независимо от пространства имен атрибута (включая пространство имен).

Последние два правила эквивалентны и будут соответствовать только элементам с атрибутом att, где атрибут не находится в пространстве имен.

Посмотрите эту скрипку, обращая внимание на стили заполнения (по умолчанию, наведите курсор и активно):
http://fiddle.jshell.net/eg43L/

API Selectors использует синтаксис селектора CSS, но не имеет эквивалента правила @namespace для определения пространства имен. В результате селектора с пространствами имен недействительны, но токен пространства имен подстановочных знаков допустим:

Если группа селекторов включает префиксы пространства имен, которые необходимо разрешить, реализация должна вызвать исключение SYNTAX_ERR ([DOM-LEVEL-3-CORE], раздел 1.4).

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

Префикс пространства имен должен быть разрешен, если компонент пространства имен не является ни пустым (например, |div), представляющим пустое пространство имен, либо звездочкой (например, *|div), представляющей любое пространство имен. Поскольку звездочка или пустой префикс пространства имен не нуждаются в разрешении, реализации, поддерживающие синтаксис пространства имен в Selectors, должны поддерживать их.

(выделено жирным шрифтом)

Завершите скрипку, на этот раз обратив внимание на вывод консоли. Команда document.querySelector('[*|href="#url"]') возвращает нужный элемент.

Одно последнее предупреждение: MDN говорит мне, что IE8 - не поддерживает пространства имен CSS, поэтому это может не сработать для них.


Обновление 2015-01-31:

Как отметил @Netsi1964 в комментариях, это не работает для пользовательских атрибутов с расширением имен в документах HTML 5, поскольку HTML не поддерживает пространства имен XML. (Он работал бы в автономном SVG или другом XML-документе, включая XHTML.)

Когда парсер HTML5 встречает такой атрибут, как data:myAttribute="value", он рассматривает это как одну строку для имени атрибута, включая :. Чтобы сделать вещи более запутанными, они автоматически уменьшают строку.

Чтобы получить querySelector, чтобы выбрать эти атрибуты, вы должны включить data: как часть строки атрибута. Однако, поскольку : имеет особое значение в CSS-селекторах, вам нужно избежать его с помощью символа \. И так как вам нужно \, чтобы пройти через часть селектора, вам нужно избежать его в вашем JavaScript.

Таким образом, успешный вызов выглядит следующим образом:

document.querySelector('[data\\:myattribute="value"]');

Чтобы сделать вещи более логичными, я бы рекомендовал использовать все нижние регистры для имен ваших атрибутов, так как парсер HTML 5 все равно их преобразует. В браузере Blink/Webkit будут выполняться автоматические нижестоящие селектора, которые вы передаете querySelector, но на самом деле это очень проблематичная ошибка (в средствах вы никогда не сможете выбрать элементы SVG с именами тегов с смешанным регистром).

Но работает ли такое же решение для xlink:href? Нет! Парсер HTML 5 распознает xlink:href в разметке SVG и правильно анализирует его как атрибут с именами.

Здесь обновленная скрипка с дополнительными тестами. Опять же, посмотрите на вывод консоли, чтобы увидеть результаты. Протестировано в Chrome 40, Firefox 35 и IE 11; единственная разница в поведении заключается в том, что Chrome соответствует селектору смешанного ввода.

Ответ 2

К сожалению, нет.

querySelector не обрабатывает пространства имен XML, поэтому нет простого способа сделать это таким образом. Однако вы можете использовать запрос XPath.

var result = document.evaluate(
    // Search for all nodes with an href attribute in the xlink namespace.
    '//*[@xlink:href="url"]',
    document,
    function(prefix){
        return {
            xlink: "http://www.w3.org/1999/xlink"
        }[prefix] || null;
    },
    XPathResult.ORDERED_NODE_ITERATOR_TYPE
);

var element = result.iterateNext();

Если вам нужна полная поддержка кросс-браузера, например, для IE, у которой нет document.evaluate, вы можете polyfill с wicked-good-xpath.

Конечно, в зависимости от вашего использования это может быть проще сделать (что, я думаю, будет работать на IE):

var element = Array.prototype.filter.call(document.querySelectorAll('a'),
    function(el){
    return el.getAttributeNS('http://www.w3.org/1999/xlink', 'href') === 'url';
})[0] || null;

Ответ 3

[*|href] будет соответствовать как html href, так и svg xlink:href, затем используйте :not([href]) для исключения html href.

document.querySelectorAll('[*|href]:not([href])')

протестирован в хроме