【问题标题】:get opening tag including attributes - outerHTML without innerHTML获取包括属性的开始标签 - 没有 innerHTML 的outerHTML
【发布时间】:2012-03-25 03:26:46
【问题描述】:

我想从 DOM 中检索某个标签元素及其属性。例如,来自

<a href="#" class="class">
  link text
</a>

我想获得&lt;a href="#" class="class"&gt;,可以选择使用结束&lt;/a&gt;,作为字符串或其他对象。 在我看来,这类似于在没有 .innerHTML 的情况下检索 .outerHTML。

最后,我需要它来通过 jQuery 包装一些其他元素。我试过了

var elem = $('#some-element').get(0);
$('#some-other-element').wrap(elem);

但.get() 返回包含其内容的DOM 元素。还有

var elem = $('#some-element').get(0);
$('#some-other-element').wrap(elem.tagName).parent().attr(elem.attributes);

失败,因为elem.attributes 返回一个NamedNodeMap,它不适用于jQuery 的attr(),我无法转换它。 承认上面的例子不是很有意义,因为它们也复制了元素的不再唯一的 ID。但是有什么简单的方法吗?非常感谢。

【问题讨论】:

  • 只需使用.clone() 克隆节点,然后使用.html() 将其清空并删除id 以及您想要的其他内容。
  • $("a").clone().empty().attr("outerHTML");解决第一个问题
  • 谢谢@kirilloid,猜猜乔希更快了;)
  • 也感谢@UlhasTuscano,应该与一些outerHTML 变通方法结合使用,例如stackoverflow.com/questions/2419749/…

标签: javascript jquery dom innerhtml outerhtml


【解决方案1】:

对于未来的 Google 员工,有一种方法可以在没有 jQuery 的情况下做到这一点:

tag = elem.outerHTML.slice(0, elem.outerHTML.indexOf(elem.innerHTML));

由于outerHTML 包含开始标记,后跟innerHTML 所包含内容的镜像,因此我们可以将outerHTML 从0(开始标记的开头)子串到innerHTML 开始的位置(开始标签的结尾),并且由于innerHTML是outerHTML的镜像,所以除了开始标签,只剩下开始标签!

这个适用于&lt;br&gt;标签、&lt;meta&gt;标签和其他空标签:

tag = elem.innerHTML ? elem.outerHTML.slice(0,elem.outerHTML.indexOf(elem.innerHTML)) : elem.outerHTML;

因为innerHTML在自闭标签中是空的,而indexOf('')总是返回0,所以上面的修改首先检查innerHTML的存在。

【讨论】:

  • 这不适用于空元素:document.createElement("strong").outerHTML.slice(0, elem.outerHTML.indexOf(elem.innerHTML))
  • @Martin 将您的createElement 声明放入一个名为elem 的变量中,并使用我上面使用的三元表达式,它返回&lt;strong&gt;&lt;/strong&gt;。
  • @AaronGillion 很好。如果 innerHTML 是一个空字符串,则可以通过搜索 &gt;&lt; 来解决这个问题。不过,这让我想到了另一个问题:&lt;span id="space"&gt; &lt;/span&gt; 标识了错误的空间索引。
  • 这很不安全。例如。当elem.outerHTML === '&lt;p&gt;p&lt;/p&gt;'。或者任何其他情况,innerHTML 的文本是 tagName 的子集或其属性之一,甚至是属性的值!
【解决方案2】:
var wrapper = $('.class').clone().attr('id','').empty();
  • 您可能希望更改选择器以更精确地匹配您要查找的 &lt;a&gt; 元素。
  • clone() 创建匹配元素的新副本,也可选择复制事件处理程序。
  • 我使用attr 清除了元素的 ID,这样我们就不会重复 ID。
  • empty() 删除所有子节点(“innerHTML”)。

【讨论】:

  • 太棒了。非常感谢,乔希 :) 对使用 .clone() 有任何顾虑吗?有人说它可能没有那么高性能,但我看不到任何其他解决方案。
  • 我猜 .clone() 并不是完全复制整个对象,并且确实存在某种引用,如果我们对克隆对象使用 .remove() ,它实际上会删除原始对象。我曾经遇到过这个问题。
  • @RichardKiefer:性能显然取决于浏览器,但这段代码ranges from 3,000 ops/sec in IE8 to 25,000 ops/sec in FF10。无论如何,您可能不应该生成数千个 DOM 元素,所以我看不出有什么可担心的。
【解决方案3】:

这是我的解决方案:

opentag=elem.outerHTML.slice(0, elem.outerHTML.length-elem.innerHTML.length-elem.tagName.length-3);

我想,结束标签的形式是:"&lt;/"+elem.tagName+"&gt;"。

【讨论】:

    【解决方案4】:

    不幸的是,@AaronGillion's answer 并不像我在my comment 中所说的那样可靠。谢谢@sus。我推荐his/her way,稍作改动即可支持&lt;self-closing tags /&gt;:

    function getOpenTag(element: HTMLElement): string {
        const outerHtml = element.outerHTML;
        const len = outerHtml.length;
        
        const openTagLength = outerHtml[len - 2] === '/' ? // Is self-closing tag?
                len :
                len - element.innerHTML.length - (element.tagName.length + 3);
        // As @sus said, (element.tagName.length + 3) is the length of closing tag. It's always `</${tagName}>`. Correct?
        
        return outerHtml.slice(0, openTagLength);
    }
    

    代码在 TypeScript 中。如果需要 JavaScript,请删除类型(HTMLElement 和 number)。

    【讨论】:

      【解决方案5】:

      如果有人没有使用 jQuery 。 . .

      elem.outerHTML
      "<a href="#" class="class">
        link text
      </a>"
      elem.cloneNode().outerHTML
      "<a href="#" class="class"></a>"
      

      如果您希望 2014 年中之前发布的 Firefox 等安全,请使用 cloneNode(false) 以避免获取内部内容。

      参考:https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode

      【讨论】:

      • 这是正确答案。应删除所有其他分散注意力的答案。
      【解决方案6】:

      这是我使用过的解决方案:

      const wrap = document.createElement('div')
      wrap.appendChild(target.cloneNode(true))
      const openingTag = wrap.innerHTML.split('>')[0] + '>'
      

      【讨论】:

        【解决方案7】:

        这可以在没有任何字符串操作的情况下完成。

        相反,您可以使用元素的内部结构并从那里自己构建字符串:

        function getTagHTML(el) {
          if (!el instanceof HTMLElement) return null;
          let result = `<${el.tagName.toLowerCase()}`;
          for (const attribute in el.attributes) {
            if (el.attributes[attribute].nodeValue) 
              result += ` ${el.attributes[attribute].name}="${el.attributes[attribute].nodeValue.replace(/"/g, "&quot;")}"`
          }
          result += `></${el.tagName.toLowerCase()}>`;
          return result;
        }
        
        console.log(getTagHTML(document.getElementById('outer')));
        <div id="outer" class='i-want-"this"'>
          <span>I do not want this</span>
        </div>

        请注意,对于像&lt;img /&gt; 这样的自闭合元素,这会给您一个不需要且不正确的结束标记。随意调整代码。

        【讨论】:

          【解决方案8】:

          +1 表示上面的 cloneNode 答案

          • 创建没有内容的 HTML
          • 循环属性
          • 可选结束标记
          • 可选的自闭合标签

          function getTagHTML(el, includeClosingTag=true , selfClosing = false) {
            //return el.cloneNode(false).outerHTML;
            if (el.attributes)
              return `<${el.localName}` + 
                [...el.attributes].map(
                       attr => ` ${attr.name}="${attr.nodeValue.replace(/"/g, "&quot;")}"`
                ).join`` +
                `${selfClosing ? "/" : ""}>` + 
                (!selfClosing && includeClosingTag ? `</${el.localName}>` : "");
            else 
              return null;
          }
          
          document.body.querySelectorAll("*:not(script)").forEach(el=>{
            console.log(getTagHTML(el));
            console.log(getTagHTML(el,false));
            console.log(getTagHTML(el,false,true))
          });
          <div id="ONE" class='CLASSNAMES' attr1='"string"'>
             INNERHTML
            </div>
            <img id="TWO" src="https://svgshare.com/i/Uhq.svg"/>

          【讨论】:

            【解决方案9】:

            我使用 Reg Exp 替换元素属性内所有可能的结束标记的解决方案:

            const outerHTML = div.outerHTML;
            const content = outerHTML.slice(0, outerHTML.length - div.innerHTML.length);
            const dummy = content.replace(/'.*?'|".*?"/g, x => 'x'.repeat(x.length));
            return content.slice(0, 1 + dummy.indexOf('>'));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-05-13
              • 2019-06-03
              • 1970-01-01
              • 2018-04-07
              • 1970-01-01
              • 2011-12-13
              • 1970-01-01
              相关资源
              最近更新 更多