如何找到起始标签或结束标签的(字符串)长度?

4
我正在尝试编写一个jQuery或纯JavaScript函数(更喜欢可读性更强的解决方案),可以计算HTML文档中起始标签或结束标签的长度。
例如:
<p>Hello.</p>

会返回起始标签长度为3,结束标签长度为4。添加属性,
<span class="red">Warning!</span>

最终结果为起始标签长度为18,结束标签长度为7。
<img src="foobar.png"/>

会返回起始标签长度为23,结束标签长度为0(或-1)。
我正在寻找一种规范的、保证按照规范工作的解决方案,因此我尝试使用 DOM 方法而不是手动文本操作。例如,我希望该解决方案即使对于奇怪的情况也能正常工作,比如:
<p>spaces infiltrating the ending tag</ p >

并且

<img alt="unended singleton tags" src="foobar.png">

这段文字的英译中文如下:

等等。也就是说,只要我们使用正确的 DOM 方法,我们应该能够找到在任何情况下位于 <> 之间的字符数,即使情况再怎么奇怪。

<div data-tag="<div>">HTML-like strings within attributes</div>

我查看了jQuery API(特别是Manipulation部分,包括DOM Insertion和General Attributes子部分),但是我没有看到任何有用的内容。
目前,我能想到的最好的主意是针对一个元素node
lengthOfEndTag = node.tagName.length + 3;

lengthOfStartTag = node.outerHTML.length
                 - node.innerHTML.length
                 - lengthOfEndTag;

当然,我不想对结束标记做出这样的假设。
(最后,我熟悉正则表达式——但尽可能地避免使用它们。)
编辑 @Pointy和@squint帮助我理解,例如无法看到</ p >,因为一旦创建DOM,HTML将被丢弃。没关系。调整后的目标是找到开始和结束标签的长度,就像在outerHTML中呈现的那样。

7
浏览器没有义务记录和披露在解析时HTML标签的源代码细节。 - Pointy
1
页面加载后,原始的HTML标记已经消失。你能做的最接近的事情就是要求浏览器读取DOM并将其观察到的内容呈现为新的HTML字符串。 - user1106925
@squint - 嗯,好的,那么让我们说我不是在谈论“原始的、未经处理的HTML”,而是outerHTML提供的HTML。那就足够了。(我会编辑我的问题。) - Andrew Cheong
2
你能详细说明一个需要它的情况吗? - Notepad
2
我很好奇你为什么想要这样做?你想要做什么? - gen_Eric
显示剩余10条评论
2个回答

1
另一种方法是使用 XMLSerializerserializeToString 在克隆节点(带有设置的id)上进行操作,以避免解析 innerHTML,然后在 "><" 上进行分割。
var tags = (function () {
    var x = new XMLSerializer(); // scope this so it doesn't need to be remade
    return function tags(elm) {
        var s, a, id, n, o = {open: null, close: null}; // spell stuff with var
        if (elm.nodeType !== 1) throw new TypeError('Expected HTMLElement');
        n = elm.cloneNode(); // clone to get rid of innerHTML
        id = elm.getAttribute('id'); // re-apply id for clone
        if (id !== null) n.setAttribute('id', id); // if it was set
        s = x.serializeToString(n); // serialise
        a = s.split('><');
        if (a.length > 1) { // has close tag
            o.close = '<' + a.pop();
            o.open = a.join('><') + '>'; // join "just in case"
        }
        else o.open = a[0]; // no close tag
        return o;
    }
}()); // self invoke to init

运行此代码后,您可以访问openclose属性的.length
tags(document.body); // {open: "<body class="question-page">", close: "</body>"}

如果属性的值中包含><XMLSerializer会将其转义为&gt;&lt;,以便不影响.split方法。
那么如果没有闭合标签呢?close将会是null

0

这个答案帮助我理解了@Pointy和@squint想要表达的意思。

以下解决方案适用于我:

$.fn.lengthOfStartTag = function () {
    var node = this[0];
    if (!node || node.nodeType != 1) {
        $.error("Called $.fn.lengthOfStartTag on non-element node.");
    }
    if (!$(node).is(":empty")) {
        return node.outerHTML.indexOf(node.innerHTML);
    }
    return node.outerHTML.length;
}

$.fn.lengthOfEndTag = function () {
    var node = this[0];
    if (!node || node.nodeType != 1) {
        $.error("Called $.fn.lengthOfEndTag on non-element node.");
    }
    if (!$(node).is(":empty")) {
        var indexOfInnerHTML = node.outerHTML.indexOf(node.innerHTML);
        return node.outerHTML.length - (indexOfInnerHTML + node.innerHTML.length);
    }
    return -1;
}

这里是一个jsFiddle示例。


你似乎在使用is(":empty")来判断标签是否是自闭合的。这不是一个好的测试,因为<div></div>也是空的。 - James Montagne
请注意,标签不会计算额外的空格。 - Daniel Moses
@DMoses - 是的,我意识到我需求的那一部分太过疯狂,而且也没有必要。不过还是谢谢你的提醒。 - Andrew Cheong
@JamesMontagne - 噢,你是对的。$('<img>').get(0).outerHTML 返回 <img><img/> 也返回 <img><div> 返回 <div></div>。我认为唯一的解决办法可能是实际上使用单例标记的列表,例如 area|br|col|embed|hr|img|input|link|meta|param。现在我会在我的答案中留下这个警告。感谢提醒。 - Andrew Cheong
@JamesMontagne - 不,等等,实际上我认为它现在很好。无论如何,如果一个元素是“空的”,则返回outerHTML的长度。最终这就是我想要的。无论如何感谢您的建议。 - Andrew Cheong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接