admin管理员组

文章数量:1318563

It appears web browsers throw a DOMException when one uses setAttributeNS on a <svg> element to set the xmlns attribute. i.e.

>>> s = document.createElementNS('', 'svg')
<svg>​</svg>​

>>> s.setAttributeNS(null, 'xmlns', '123')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

>>> s.setAttributeNS('', 'xmlns', 
      '')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

>>> s.setAttributeNS(null, 'xmlns', '')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

The Mozilla docs remend always using setAttributeNS, however it makes no mention of this possibility. So the remendation appears to have caveats.

The DOM Level 2 spec on setAttributeNS gives some insight:

NAMESPACE_ERR: Raised if the qualifiedName is malformed, if the qualifiedName has a prefix and the namespaceURI is null, if the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "", or if the qualifiedName is "xmlns" and the namespaceURI is different from "/".

So this particular exception appears to be part of a broader set of cases that could fail. It's not immediately apparent what those cases are.

I'm writing tko / Knockout 4.0, a general-purpose web framework, and so it should support svg and other tags outside the core HTML namespace.

The most encountered problem es from xmlns on svg tags so it's an issue. I've worked around this by specifically checking to see if xmlns is being set and using setAttribute in that case.

That workaround seems quite specific and I'm concerned about the general case. Is there a precedent for how to generally handle setting attributes with setAttributeNS and setAttribute?

The other web frameworks don't address this neatly — it's generally mixed with other logic; the most on-point mit I've seen is for angular, but it's not directly addressing this problem.

Related: Difference between setAttribute and setAttributeNS(null,

It appears web browsers throw a DOMException when one uses setAttributeNS on a <svg> element to set the xmlns attribute. i.e.

>>> s = document.createElementNS('http://www.w3/2000/svg', 'svg')
<svg>​</svg>​

>>> s.setAttributeNS(null, 'xmlns', '123')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

>>> s.setAttributeNS('http://www.w3/2000/svg', 'xmlns', 
      'http://www.w3/2000/svg')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

>>> s.setAttributeNS(null, 'xmlns', 'http://www.w3/2000/svg')
Uncaught DOMException: Failed to execute 'setAttributeNS' 
  on 'Element': '' is an invalid namespace for attributes.

The Mozilla docs remend always using setAttributeNS, however it makes no mention of this possibility. So the remendation appears to have caveats.

The DOM Level 2 spec on setAttributeNS gives some insight:

NAMESPACE_ERR: Raised if the qualifiedName is malformed, if the qualifiedName has a prefix and the namespaceURI is null, if the qualifiedName has a prefix that is "xml" and the namespaceURI is different from "http://www.w3/XML/1998/namespace", or if the qualifiedName is "xmlns" and the namespaceURI is different from "http://www.w3/2000/xmlns/".

So this particular exception appears to be part of a broader set of cases that could fail. It's not immediately apparent what those cases are.

I'm writing tko / Knockout 4.0, a general-purpose web framework, and so it should support svg and other tags outside the core HTML namespace.

The most encountered problem es from xmlns on svg tags so it's an issue. I've worked around this by specifically checking to see if xmlns is being set and using setAttribute in that case.

That workaround seems quite specific and I'm concerned about the general case. Is there a precedent for how to generally handle setting attributes with setAttributeNS and setAttribute?

The other web frameworks don't address this neatly — it's generally mixed with other logic; the most on-point mit I've seen is for angular, but it's not directly addressing this problem.

Related: Difference between setAttribute and setAttributeNS(null,

Share Improve this question edited Sep 29, 2018 at 19:07 Brian M. Hunt asked Sep 29, 2018 at 18:06 Brian M. HuntBrian M. Hunt 83.9k76 gold badges234 silver badges349 bronze badges 8
  • When I create an SVG I use var svg = document.createElementNS(ns.svg, "svg"); where ns.svg: "http://www.w3/2000/svg" – enxaneta Commented Sep 29, 2018 at 18:37
  • Wouldn't it be better to force pliance to the spec and set the xmlns namespaceURI if it's encountered - and, for the same reason, set the xml namespaceURI if it has been used as a prefix? – ccprog Commented Sep 29, 2018 at 18:43
  • @ccprog TKO does indeed set the xmlns according to the spec, and that is the better approach, but that does not solve the problem. – Brian M. Hunt Commented Sep 29, 2018 at 18:45
  • i.e. document.createElementNS('http://www.w3/2000/svg', "svg").setAttributeNS(null, 'xmlns', '...') throws – Brian M. Hunt Commented Sep 29, 2018 at 18:46
  • What I meant was, in the method you quoted, something like var ns = name === 'xmlns' ? 'http://www.w3/2000/xmlns/' : null; node.setAttributeNS(ns, name, String(value)) – ccprog Commented Sep 29, 2018 at 18:51
 |  Show 3 more ments

1 Answer 1

Reset to default 7

It will not cover all cases, but this should go a long way:

const NAMESPACES = {
  svg: 'http://www.w3/2000/svg',
  html: 'http://www.w3/1999/xhtml',
  xml: 'http://www.w3/XML/1998/namespace',
  xlink: 'http://www.w3/1999/xlink',
  xmlns: 'http://www.w3/2000/xmlns/' // sic for the final slash...
}

class JsxObserver extends LifeCycle {
  ...

  setNodeAttribute (node, name, valueOrObservable) {
    const value = unwrap(valueOrObservable)
    NativeProvider.addValueToNode(node, name, valueOrObservable)
    if (value === undefined) {
      node.removeAttributeNS(null, name)
    } else if (isThenable(valueOrObservable)) {
      Promise.resolve(valueOrObservable)
        .then(v => this.setNodeAttribute(node, name, v))
    } else {
      const [prefix, ...unqualifiedName] = name.split(':')
      let ns = null
      if (prefix === 'xmlns' || unqualifiedName.length && NAMESPACES[prefix]) {
        ns = NAMESPACES[prefix]
      }
      node.setAttributeNS(ns, name, String(value))
    }
  }
}

If the attribute encountered is xmlns="http://www.w3/2000/svg", it will be added with

.setAttributeNS('http://www.w3/2000/xmlns/', 'xmlns', 'http://www.w3/2000/svg')

If the attribute encountered is xml:space="preserve" (something that SVG editors notoriously use), it will be added with

.setAttributeNS('http://www.w3/XML/1998/namespace', 'xml:space', 'preserve')

本文标签: javascriptsetAttributeNS xmlns of ltsvggt for a generalpurpose libraryStack Overflow