.getElementById 的内联 SVG 和 HTML DOM 范围

Posted

技术标签:

【中文标题】.getElementById 的内联 SVG 和 HTML DOM 范围【英文标题】:Inline SVG and HTML DOM Scope for .getElementById 【发布时间】:2013-09-10 01:15:21 【问题描述】:

问题空间:

我有一个 SVG 地图,我使用 JQUERY Ajax 将其嵌入到 html 中。然后我使用 java 脚本来处理 svg 对象中的许多元素,以更改属性并添加基于数据系列的事件侦听器。数据和地图是交互的。用户可以通过多种排列更改状态。

我也在使用 JQUERY UI 1.10.3 选项卡

有很多类别。因此,根据演示文稿的不同,同一选项卡上可能有多个 svg 地图副本,并且许多不同的选项卡具有相同的 svg 地图。

svg 文档的每个副本都具有相同的元素 ID。我遇到的问题是元素 ID 在 HTML DOM 中相互冲突。因此,唯一可寻址的元素是 svg 对象中最先出现在 HTML 文档中的元素。

我的问题是:

我是否需要为 svg 对象的每个实例设置唯一的元素 ID?或者有没有办法在 HTML DOM 中为 svg 对象设置范围和上下文?

示例:

每个包含的 div 都有一个唯一的 ID,我用它来插入 svg 文档和检索 SVG 文档。

    <div id="svgMap_div_1" class="cIG" style="height: 500px; width: 750px"></div>

用于加载从 ajax 接收的 svg 文档的代码(mimeType = "image/svg+xml"):

    document.getElementById('svgMap_div_1').appendChild(svgXMLObject.documentElement)

用于检索 svg 文档的代码。 svgDoc 的声明对于实现它的函数来说是本地的并且是唯一的。

    var svgDoc = document.getElementById('svgMap_div_1').ownerDocument;

用于更新单个元素的代码。当相同的 svg 对象(子元素具有相同的 ID)位于同一个 HTML DOM 中时,即使从离散的 div 中检索到,此 javascript 也将始终处理 HTML DOM 中首先出现的 svg 对象,例如 id=' svgMap_div_2'。

    var svgElem = svgDoc.getElementById('elemID');
    svgElem.setAttribute("fill", '#0000FF');

我认为问题的根源在于 svgDoc 是使用 ownerDocument 检索的。它不是创建具有范围和上下文的离散对象,而是贯穿整个 DOM,使 DOM 成为上下文。请参阅 IDE 中的代码参考:

HTMLDocument Node.ownerDocument

    Property ownerDocument http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html 
    See Also:
    Document
    Since:
    Standard ECMA-262 3rd. Edition
    Level 2 Document Object Model Core Definition.
    @type
    Document

如果需要,我可以开发一种有效的算法来使所有 id 都独一无二,但我肯定想找到更好的方法。我已经尝试过其他方法和属性来获得一个有效的 svg 文档对象,但这些都不适合我。

下面是一个工作示例,用于演示当两个 svg 对象位于同一页面上的不同 div 时会发生什么。所有四种组合都会改变第一个矩形的颜色。如果您将第二个对象中矩形的 ID 更改为“box-b”并相应地对其进行寻址(“bbox_div”、“box-b”),则两个矩形将相互独立地进行操作。

<script type='text/javascript'>
function colorBox(divID, svgElem, colorHex) 
    /* DOM  This is the original solution that did not work !!!*/
    //    var svgDoc = document.getElementById(divID).ownerDocument.
    //    var svgBox = svgDoc.getElementById(svgElem);
    //    svgBox.setAttribute("fill", colorHex);

    /* querySelector */
    //    document.querySelector("#"+divID + " ."+svgElem).setAttribute("fill", colorHex);

    /* jquery div */
    //    var svgBox = $('#'+divID+' svg').find('#'+svgElem);
    //    svgBox.css("fill", colorHex);    

    /* jquery class */
    $('#'+divID+' svg').find('.'+svgElem).css("fill", colorHex);

</script>
<div style="height: 100px; width: 100px">
    <a href="#" onclick="colorBox('abox_div', 'box-a', '#FF0000');return false;">Box A Red</a><br>
    <a href="#" onclick="colorBox('abox_div', 'box-a', '#0000FF');return false;">Box A Blue</a><br>
    <a href="#" onclick="colorBox('bbox_div', 'box-a', '#FF0000');return false;">Box B Red</a><br>
    <a href="#" onclick="colorBox('bbox_div', 'box-a', '#0000FF');return false;">Box B Blue</a><br -->
</div>
<div id="abox_div" style="height: 100px; width: 100px">
    <svg xmlns="http://www.w3.org/2000/svg"
         
          
         viewBox="0 0 71.3461 42.9997">

    <style type="text/css">
    <![CDATA[
        .allboxes fill:#00FF00
    ]]>
    </style>

    <g class="box-a allboxes" transform="translate(0.24,-0.24)"  fill="#e8eef7">
        <rect x="0" y="0.48"   ><title>Box A</title></rect>
    </g>
   </svg>
</div>
<div id="bbox_div" style="height: 100px; width: 100px">
   <svg xmlns="http://www.w3.org/2000/svg"
        
         
        viewBox="0 0 71.3461 42.9997">
    <g class="box-a allboxes" transform="translate(0.24,-0.24)"  fill="#e8eef7">
        <rect x="0" y="0.48"   ><title>Box B</title></rect>
    </g>
   </svg>
</div>

【问题讨论】:

消除冲突 id 的最终解决方案采用 JQUERY .class 选择器,并具有多个用于唯一和通用样式属性分配的类。请参阅原始问题中的更新工作示例。 【参考方案1】:

如果您有重复的 ID,我相信严格来说它是无效的 HTML。我建议使用其他一些识别元素的机制,使用类而不是 ID 或 data- 属性。然后你可以从.getElemntById() 移动到.querySelector() (try on JS Bin):

<html>
<head>
<title></title>
</head>
<body>
  <div id="div1">
    <svg xmlns="http://www.w3.org/2000/svg"  >
      <rect class="rect1"  />
    </svg>
  </div>
  <div id="div2">
    <svg xmlns="http://www.w3.org/2000/svg"  >
      <rect class="rect1"  />
    </svg>
  </div>
  <script type="text/javascript">
    document.querySelector("#div1 .rect1").setAttribute("fill","red");
    document.querySelector("#div2 .rect1").setAttribute("fill","green");
  </script>
</body>
</html>

.getElementsByClassName() (try on JS Bin):

<html>
  <head>
    <title></title>
  </head>
  <body>
    <div id="div1">
      <svg xmlns="http://www.w3.org/2000/svg"  >
        <rect class="rect1"  />
      </svg>
    </div>
    <div id="div2">
      <svg xmlns="http://www.w3.org/2000/svg"  >
        <rect class="rect1"  />
      </svg>
    </div>
    <script type="text/javascript">
      document.getElementById("div1").getElementsByClassName("rect1")[0].setAttribute("fill","red");
      document.getElementById("div2").getElementsByClassName("rect1")[0].setAttribute("fill","green");
    </script>
  </body>
</html>

如果您保持 ID 属性不变,.querySelector() 可能也会起作用,例如:

document.querySelector("#div1 [id=rect1]")

但我仍然建议使用 data- 属性,因为这样可以避免任何潜在的 ID 陷阱。

【讨论】:

谢谢。我将遵循您的指导,避免创建与 HTML 5 规范不一致且可能导致难以解决的错误的冲突 ID(不排除当前情况)。我仍然希望能够跨多个元素定义样式属性,我可以使用多个类 ID。所以最终的解决方案是使用带有 .class 选择器和多个类 id 的 JQUERY。【参考方案2】:

由于你已经在使用 jQuery,你可以使用 jQuery 的选择器,它不关心重复的 id。

function colorBox(divID, svgElem, colorHex) 
  var svgBox = $('#'+divID+' svg').find('#'+svgElem);
  svgBox.css("fill", colorHex);

在这里演示:http://jsfiddle.net/w9KA3/1/

【讨论】:

谢谢。这确实解决了问题。我将稍微修改您的解决方案以使用 .class 选择器,如下所示。在确保跨浏览器和版本的更大可移植性的前提下,我将尽可能使用 JQuery 进行 DOM 操作。我有兴趣听取反驳意见、该政策的潜在风险或陷阱。

以上是关于.getElementById 的内联 SVG 和 HTML DOM 范围的主要内容,如果未能解决你的问题,请参考以下文章

内联 SVG 与 SVG 文件性能

SVG 中的内联文本编辑

Safari 和 Mobile Safari 中的内联 SVG 中断

SVG的HTML5内联

具有完美定位的叠加层和顶部文本的内联 SVG 元素

内联 SVG 随 Flexbox 消失