javascript - 合并嵌套、重叠的 <strong> 和 <em> 标签

标签 javascript html

我有一个文本字符串,我正在单独存储标记。例如:

var content = {
    text: "a little white rabbit hops",
    style: [
        {
            type: "strong",
            start: 0,
            length: 8
         },
         {
            type: "em",
            start: 2,
            length: 14
         }
    ]
}

然后我将其解析为 html,但是 em 标签必须打开和关闭两次才能正确格式化:

<strong>a <em>little</em></strong><em> white</em> rabbit hops

我的问题是:解析从 DOM 检索的 html 以合并分离的 em 标签(或者可以想象的 strong 标签)的最佳方法是什么:在我的场景中两者都可以嵌套)。

如果我迭代子节点的 NodeList (p.getElementsByTagName('em')),我将不得不执行多个 for 循环并检查所有嵌套标签的开始/长度。必须有一种更简单的方法,但我还没有想到 - 是否有一个库可以处理这种格式(或者直接通过 DOM 执行此操作的方法)?

我没有使用 jQuery 并且不想为此将其添加到我的项目中。非常感谢任何帮助!

---编辑---

为了澄清这个问题:这本质上是关于将格式转换为 HTML 或从 HTML 中转换出来,问题是处理标签嵌套的最佳方法:即即使有两个 em 子标签,实际上只有一个 em 格式的 block (em 子标签 1 和 2 的结束/开始是连续的)

最佳答案

这里有两个函数用于任一方向的转换。

第一个将 HTML 字符串转换为您描述的内容结构:

function htmlToContent(html) {
    // The object to fill and return:
    var content = {
        text: '',
        style: []
    };
    // Keep track of recently closed tags (i.e. without text following them as of yet)
    var closedStyles = [];

    // Recursive function
    function parseNode(elem) {
        var style;
        if (elem.nodeType === 3) {
            // This is a text node (no children)
            content.text += elem.nodeValue;
            // Any styles that were closed should be added to the content 
            // style array, as they cannot be "extended" any more
            [].push.apply(content.style, closedStyles);
            closedStyles = [];
        } else {
            // See if we can extend a style that was closed
            if (!closedStyles.some(function (closedStyle, idx) {
                if (closedStyle.type === elem.nodeName) {
                    style = closedStyle;
                    // Style will be extended, so it's no longer closed
                    closedStyles.splice(idx, 1);
                    return true; // exit ".some"
                }
            })) {
                // No style could be extended, so we create a new one
                style = {
                    type: elem.nodeName,
                    start: content.text.length,
                    length: 0
                };
            }
            // Recurse into the child nodes:
            [].forEach.call(elem.childNodes, function(child) {
                parseNode(child);
            });
            // set style length and store it as a closed one
            style.length = content.text.length - style.start;
            closedStyles.push(style);
        }
    }
    // Create a node with this html
    wrapper = document.createElement('p');
    wrapper.innerHTML = html;
    parseNode(wrapper);
    // Flush remaining styles to the result
    closedStyles.pop(); // Discard wrapper
    [].push.apply(content.style, closedStyles);
    return content;
}

该函数首先将 HTML 字符串注入(inject)到 DOM 包装元素中,然后递归到节点层次结构中以构建内容结构。此代码的主要思想是,它首先在临时 latedStyles 数组中收集闭合节点。仅当确定这些不能再用于与即将到来的节点合并时,它们才会添加到内容结构中。当遇到文本节点时会发生这种情况。但是,如果标签在没有中间文本的情况下关闭并再次打开,则将从该 latedStyles 数组中定位并提取匹配的样式,并重新用于扩展。

执行相反操作的函数可以定义如下:

function contentToHtml(content) {
    var tags = [];
    // Build list of opening and closing tags with the offset of injection
    content.style.forEach(function (tag) {
        tags.push({
            html: '<' + tag.type + '>',
            offset: tag.start
        }, {
            html: '</' + tag.type + '>',
            offset: tag.start + tag.length
        });
    });
    // Sort this list by decreasing offset:
    tags.sort(function(a, b) {
        return b.offset - a.offset;
    });
    var html = '';
    var text = content.text;
    // Insert opening and closing tags from end to start in text
    tags.forEach(function (tag) {
        // Prefix the html with the open/close tag and the escaped text that follows it
        html = tag.html + textToHtml(text.substr(tag.offset)) + html;
        // Reduce the text to the part that still needs to be processed
        text = text.substr(0, tag.offset);
    });
    // Remaining text:
    html = textToHtml(text) + html;
    // Create a node with this html, in order to get valid html tag sequences
    p = document.createElement('p');
    p.innerHTML = html;
    // p.innerHTML will change here if html was not valid.
    return p.innerHTML;
}

此函数首先将每种样式转换为两个对象,一个表示开始标记,另一个表示结束标记。然后将这些标签插入到文本的正确位置(从文本的那时到开头)。最后应用了您自己描述的技巧:将生成的 html 放入 dom 对象中并再次从中取出。这样,任何无效的 HTML 标记序列都会被修复。

该函数使用textToHtml实用函数,其定义如下:

function textToHtml(text) {
    // See http://www.w3.org/International/questions/qa-escapes#use
    return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;');
}

您可以在 fiddle 中看到它的工作原理,其中使用了示例 HTML 字符串,该字符串还包含相同类型的嵌套标签。这些都得到维护。

关于javascript - 合并嵌套、重叠的 <strong> 和 <em> 标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33879378/

相关文章:

javascript - 从循环中的文本数组分页 HTML textarea 内容

javascript - 从 2 个不同的属性获取 MySql 表中的项目

javascript - 跨 html 文件访问 javascript 变量

javascript - 单击按钮时动态创建的 div 行为

javascript - 如何将 Google Analytics 对象放入 PHP 脚本之间的 $_SESSION 变量中? (非对象错误)

javascript - 我没有得到这个 js 代码的任何输出

Javascript 正则表达式检测字符串中的 CSS 代码?

javascript - jQuery 切换列表代码冗余

javascript - Onclick 函数打开新窗口并计算点击次数

javascript - 并排响应图像具有相同的高度不同的宽度