¿Es posible utilizar .querySelector() de HTML para seleccionar por atributo xlink en un SVG?

StackOverflow https://stackoverflow.com//questions/23034283

Pregunta

Dado:

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

¿Es posible utilizar HTML DOM? .querySelector() o .querySelectorAll() para seleccionar el enlace dentro del SVG por el contenido de su xlink:href ¿atributo?

Esto funciona:

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

Estos no:

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

¿Hay alguna forma de escribir ese selector de atributos para que "vea" el xlink:href?

¿Fue útil?

Solución

Selector de consultas poder manejar espacios de nombres, pero se vuelve complicado porque

  1. La sintaxis para especificar espacios de nombres en los selectores CSS es diferente a la de HTML;

  2. La API querySelector no tiene ningún método para asignar un prefijo de espacio de nombres (como xlink) a un espacio de nombres real (como "http://www.w3.org/1999/xlink").

Sobre el primer punto, la parte relevante de las especificaciones CSS le permite especificar No espacio de nombres (el predeterminado), un espacio de nombres específico o cualquier espacio de nombres:

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

La primera regla coincidirá sólo con elementos con el atributo att en el "http://www.ejemplo.com"espacio de nombres con el valor "val".

La segunda regla coincidirá sólo con elementos con el atributo att independientemente del espacio de nombres del atributo (incluido ningún espacio de nombres).

Las dos últimas reglas son equivalentes y sólo coincidirán con elementos con el atributo att donde está el atributo no en un espacio de nombres.

Vea este violín, prestando atención a los estilos de relleno (predeterminado, flotante y activo):
https://jsfiddle.net/eg43L/

La API de selectores adopta la sintaxis del selector CSS, pero no tiene equivalente a la @namespace regla para definir un espacio de nombres.Como resultado, Los selectores con espacios de nombres no son válidos. pero el token del espacio de nombres comodín es válido:

Si el grupo de selectores incluye prefijos de espacios de nombres que deben resolverse, la implementación debe generar una excepción SYNTAX_ERR ([DOM-LEVEL-3-CORE], sección 1.4).

Esta especificación no proporciona soporte para resolver prefijos de espacios de nombres arbitrarios.Sin embargo, se puede considerar la inclusión de soporte para un mecanismo de resolución de prefijo de espacio de nombres en una versión futura de esta especificación.

Es necesario resolver un prefijo de espacio de nombres si el componente del espacio de nombres no está vacío (p. ej. |div), que representa el espacio de nombres nulo o un asterisco (p. ej. *|div), que representa cualquier espacio de nombres. Dado que no es necesario resolver el asterisco o el prefijo de espacio de nombres vacío, las implementaciones que admiten la sintaxis de espacio de nombres en Selectores deben admitirlos.

(negrita agregada)

Verificar el violín Nuevamente, esta vez prestando atención a la salida de la consola.El comando document.querySelector('[*|href="#url"]') devuelve el elemento que desea.

Una última advertencia: MDN me dice que IE8 no admite espacios de nombres CSS, por lo que es posible que esto no funcione para ellos.


Actualización 2015-01-31:

Como @Netsi1964 señaló en los comentarios, esto no funciona para atributos de espacios de nombres personalizados en documentos HTML 5, ya que HTML no admite espacios de nombres XML.(Funcionaría en un SVG independiente u otro documento XML, incluido XHTML).

Cuando el analizador HTML5 encuentra un atributo como data:myAttribute="value" lo trata como una sola cadena para el nombre del atributo, incluido el :.Para hacer las cosas más confusas, pone la cadena en minúsculas automáticamente.

Llegar querySelector Para seleccionar estos atributos, debe incluir el data: como parte de la cadena de atributos.Sin embargo, desde el : tiene un significado especial en los selectores CSS, debes escaparlo con un \ personaje.Y como necesitas el \ Para pasar como parte del selector, debes escapar. él en tu JavaScript.

Por lo tanto, la llamada exitosa se ve así:

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

Para hacer las cosas un poco más lógicas, recomendaría usar minúsculas para los nombres de sus atributos, ya que el analizador HTML 5 los convertirá de todos modos.El navegador Blink/Webkit automáticamente seleccionará minúsculas cuando pase querySelector, pero en realidad es un error muy problemático (significa que nunca se pueden seleccionar elementos SVG con nombres de etiquetas en mayúsculas y minúsculas mixtas).

¿Pero funciona la misma solución para xlink:href?¡No!El analizador HTML 5 reconoce xlink:href en el marcado SVG y lo analiza correctamente como un atributo de espacio de nombres.

Aquí está el violín actualizado con pruebas adicionales..Nuevamente, mire la salida de la consola para ver los resultados.Probado en Chrome 40, Firefox 35 e IE 11;la única diferencia en el comportamiento es que Chrome coincide con el selector de mayúsculas y minúsculas.

Otros consejos

Lamentablemente no.

querySelector no maneja espacios de nombres XML, por lo que no existe una manera fácil de hacerlo de esa manera.Sin embargo, puedes utilizar un XPath consulta.

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();

Si necesita compatibilidad total con varios navegadores, como IE, que no tiene document.evaluate, puedes rellenarlo con malvado-bueno-xpath.

Por supuesto, dependiendo de su uso, puede ser más fácil hacer esto (que creo que funcionará en 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;

[*|href] coincidirá con ambos html href y svg xlink:href, luego usa :not([href]) excluir html href.

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

probado en cromo

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top