【问题标题】:How to remove H1 formatting within ContentEditable (wysiwyg)如何在 ContentEditable 中删除 H1 格式(所见即所得)
【发布时间】:2013-08-20 14:02:45
【问题描述】:

除了使用撤消之外,我认为没有办法删除可编辑内容中的 h1 和 h2 标记。预期的行为是再次单击 H1 按钮应该将其关闭,但事实并非如此。还有一个“删除格式”按钮,但它只适用于粗体、斜体等项目。有没有办法通过 javascript 做到这一点?

编辑:结果必须删除开始和结束 H1 标签,而不是用其他任何东西替换它。

请在此处查看简化的测试用例: http://jsfiddle.net/kthornbloom/GSnbb/1/

<div id="editor" contenteditable="true">
    <h1>This is a heading one</h1>
    How can I remove the header styling if I want to?
</div>

【问题讨论】:

  • jsfiddle 链接已损坏
  • 对不起,我最近清理了一些旧小提琴。不过,已接受答案中的那个仍然有效。

标签: javascript html contenteditable


【解决方案1】:

我决定实施我在对其他答案的评论中概述的方法:遍历所选范围内的节点并删除特定节点(在这种情况下,基于标签名称)。

这是完整的演示。它不适用于 IE

http://jsfiddle.net/gF3sa/1/

此示例包含来自elsewhere on SO 的修改范围遍历代码。

function nextNode(node) {
    if (node.hasChildNodes()) {
        return node.firstChild;
    } else {
        while (node && !node.nextSibling) {
            node = node.parentNode;
        }
        if (!node) {
            return null;
        }
        return node.nextSibling;
    }
}

function getRangeSelectedNodes(range, includePartiallySelectedContainers) {
    var node = range.startContainer;
    var endNode = range.endContainer;
    var rangeNodes = [];

    // Special case for a range that is contained within a single node
    if (node == endNode) {
        rangeNodes = [node];
    } else {
        // Iterate nodes until we hit the end container
        while (node && node != endNode) {
            rangeNodes.push( node = nextNode(node) );
        }

        // Add partially selected nodes at the start of the range
        node = range.startContainer;
        while (node && node != range.commonAncestorContainer) {
            rangeNodes.unshift(node);
            node = node.parentNode;
        }
    }

    // Add ancestors of the range container, if required
    if (includePartiallySelectedContainers) {
        node = range.commonAncestorContainer;
        while (node) {
            rangeNodes.push(node);
            node = node.parentNode;
        }
    }

    return rangeNodes;
}

function getSelectedNodes() {
    var nodes = [];
    if (window.getSelection) {
        var sel = window.getSelection();
        for (var i = 0, len = sel.rangeCount; i < len; ++i) {
            nodes.push.apply(nodes, getRangeSelectedNodes(sel.getRangeAt(i), true));
        }
    }
    return nodes;
}

function replaceWithOwnChildren(el) {
    var parent = el.parentNode;
    while (el.hasChildNodes()) {
        parent.insertBefore(el.firstChild, el);
    }
    parent.removeChild(el);
}

function removeSelectedElements(tagNames) {
    var tagNamesArray = tagNames.toLowerCase().split(",");
    getSelectedNodes().forEach(function(node) {
        if (node.nodeType == 1 &&
                tagNamesArray.indexOf(node.tagName.toLowerCase()) > -1) {
            // Remove the node and replace it with its children
            replaceWithOwnChildren(node);
        }
    });
}

removeSelectedElements("h1,h2,h3,h4,h5,h6");

【讨论】:

  • 这看起来很不错。如果您不介意一个问题 - 似乎您需要选择的不仅仅是标题文本才能删除它。我认为这是因为从技术上讲,光标在结束 H1 标记之前结束。那是对的吗?有什么办法可以解决这个问题?
  • @kthornbloom:啊,这是我的疏忽。我会解决的。
  • @kthornbloom:已更新。
  • 完美,仍然有效,感谢@TimDown!我不得不重写'replaceWithOwnChildren',因为它给出了'el.parentNode is null',原因不明。这对我有用:let $contents = $(el).html(); $(el).replaceWith($contents);还必须在 'getSelectedNodes().forEach(function(node)' 之后的行上包装一个 'if(node)',因为它在某些情况下会给出错误 'node is null'。
【解决方案2】:

这可能无法完全满足您的需求,但您可以使用FormatBlock 命令并传入“div”或“pre”作为最终参数:

document.execCommand('formatBlock', false, 'p');

演示:http://jsfiddle.net/GSnbb/2/ [jsFiddle已被删除]

编辑:是的,这并不能像现在这样回答问题。但是,它早于关于不替换 &lt;h1&gt; 元素的问题的编辑,并且是对原始问题的合理回答。

【讨论】:

  • 这几乎行得通。我真的在尝试构建一些不会产生糟糕代码的东西。如果 execCommand 没有帮助,使用 javascript 保存选择的任何内容,剥离 html,然后将其返回到编辑区域听起来是否可行?
  • @CtotheDoubleS:是的:获取选定的块元素并删除它们应该不会太难,尽管我没有一个简单的例子。
  • 查看 DOM 检查器,这不会产生像嵌套 &lt;h1&gt;&lt;p&gt;No heading!&lt;/p&gt;&lt;/h1&gt; 或任何东西这样的糟糕代码,所以我认为这可以解决您的问题 :)
  • jsfiddle 链接已损坏 :(
  • @STEVER:原来如此。我假设拥有该 jsFiddle 的 OP 已将其删除。我应该自己做的,现在它迷失在互联网的迷雾中。
【解决方案3】:

用javascript是可行的,逻辑如下:

  1. 获取选定的文本及其位置(cf. Get the Highlighted/Selected textjavascript - Getting selected text position)
  2. 从所选文本中删除所有&lt;h1&gt;&lt;/h1&gt;

    s = s.replace(/<h1>/g, '');  
    s = s.replace(/<\/h1>/g,''); 
    
  3. 插入更正的文本代替原来的文本

我已经起草了一个solution based on your JSFiddle,但它需要一些调整。

有效:从 Gecko 和基于 webKit 的浏览器上的选定文本中删除 &lt;h1&gt;&lt;/h1&gt;

未开发:IE 支持 - 参见。 jsfiddle中的链接,应该不难

坏了

  • 替换不完整的选择(仅包含 &lt;h1&gt;&lt;/h1&gt; 之一) - 易于修复
  • 删除位于所选文本开头的&lt;h1&gt; - 您需要更多地使用selectionsranges 来解决这个问题。

附:您是否考虑过使用现有的文本编辑器插件而不是自己创建它?

【讨论】:

  • 我确实遇到了选定的文本帖子。我只是无法在按下按钮时将它们全部串起来。如果您知道怎么做,将获得 50 个代表点!
  • 我并不太关心 IE 支持,尽管我正在 Chrome 29 中进行测试,但它似乎也没有在那里工作。当我突出显示 H1 标题并单击按钮时,什么也没有发生。不过,这是一个好的开始,因此对此表示赞成。您在 webkit 中的演示是否成功?实际上,我有一个非常好的编辑器,它可以解决大多数典型问题。这只是我需要解决的最后一个错误!
  • 它在 Chrome 中适用于我,但您需要在 &lt;h1&gt; 之前开始选择并在 &lt;/h1&gt; 之后完成选择。参看。第二个“破碎”的要点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多