【问题标题】:Insert Table into content editable div将表格插入内容可编辑的 div
【发布时间】:2012-11-09 00:00:59
【问题描述】:

我有一个 fiddle 显示我的代码在做什么。使用 javascript/jquery 我正在尝试将表格插入到当前插入符号位置的内容可编辑 div 中。我正在使用Tim Down's Rangy 库来完成此操作。我正在使用以下 javascript 执行此操作。

var range = getFirstRange();
var el = document.createElement("table");
var tableHtml = "";
for (var a = 0; a <= tableY; a++) {
    if(a%2==0){
       tableHtml += '<tr class="zebra">';
    }
    else{
       tableHtml += '<tr>';
    }
    for (var b = 0; b <= tableX; b++) {
       tableHtml += '<td>&nbsp;</td>';
    }
    tableHtml += '</tr>';   
}
$(el).html(tableHtml); 
range.insertNode(el);
rangy.getSelection().setSingleRange(range);

getFirstRange 函数以防万一。

function getFirstRange() {
   var sel = rangy.getSelection();
   return sel.rangeCount ? sel.getRangeAt(0) : null;
} 

我需要在放置此表的任何位置制作有效的 html。例如,如果插入符号位于链接中间,我会尝试避免使用以下 html。

<p>some text <a href="#">text 
                         <table>
                             <tr>
                               <td>table content</td>
                             </tr>
                         </table> 
              text</a> more text</p> 

我希望它看起来像这样。

<p>some text <a href="#">text</a></p>
<table>
   <tr>
     <td>table content</td>
   </tr>
</table>
<p><a href="#">text</a> more text</p>

【问题讨论】:

  • 我的回答似乎没有抓住重点。您希望将插入的表格放置在该精确位置,但关闭并重新打开它周围的所有无效标签?
  • @svidgen 没错!
  • 您是否注意到像 Safari 这样的浏览器会尝试自动更正源代码,从而使 JavaScript 匹配与您的实际源代码不同。这在某种程度上是好的,但在您想要修改结构的情况下,当然也很烦人。在我的 Safari 中,您的代码已自动更改为; &lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt; &lt;table&gt;&lt;/table&gt; &lt;a&gt;&lt;/a&gt;。所以现在你几乎拥有了你已经想要实现的目标,但最后一个&lt;a&gt;&lt;/a&gt; 没有被&lt;p&gt; 包裹。浏览器这样做是因为它不能在行内元素中容纳块和怪异display-modes(如表格)。
  • 检查我的小提琴(以及它返回的&lt;body&gt; 块(是的,表格的父级神奇地变成了主体:P);jsfiddle.net/Allendar/jBrxb
  • 50 名声,用于实现可以改变目标范围以包含任何抽象 DOM 节点的 DOM 或 SAX 解析器。 github.com/tinymce/tinymce/tree/master/jscripts/tiny_mce/…

标签: javascript jquery html-table rangy


【解决方案1】:

如果您想在无法有效包含它的选定节点之后立即删除新节点,请替换为:

range.insertNode(el);

有了类似的东西:

var badNodes = {a: 1, p: 1};

// starting with the node at the beginning of the range,
// iterate to the "left" until we find a node that isn't
// a text node
var n = range.startContainer;
var tag = n.nodeName;
while (tag == '#text') {
    n = n.parentNode;
    tag = n.nodeName;
}

// if the node we landed on isn't one of our bad nodes ...
if (badNodes[tag.toLowerCase()]) {

    // that we refuse to insert 'el' into, continue iterating to the
    // "left" until we find a node we're willing to place 'el' after.
    while (badNodes[n.parentNode.nodeName.toLowerCase()]) {
        n = n.parentNode;
        tag = n.nodeName;
    }
    n.parentNode.insertBefore(el, n.nextSibling);

} else {
    range.insertNode(el);
}

见我的小提琴叉:http://jsfiddle.net/zntwL/29/


更新 (我想这就是你想要的)

如果您想拆分无效节点并将新节点放入,请改用以下内容:

var badNodes = {a: 1, p: 1};

// starting with the node at the beginning of the range,
// iterate to the "left" until we find a node that isn't
// a text node
var n = range.startContainer;
var tag = n.nodeName;
while (tag == '#text') {
    n = n.parentNode;
    tag = n.nodeName;
}

// if the node we landed on is one of our "bad" nodes ...
if (badNodes[tag.toLowerCase()]) {

    // continue iterating to the "left" until we find a "good" node
    while (badNodes[n.parentNode.nodeName.toLowerCase()]) {
        n = n.parentNode;
        tag = n.nodeName;
    }

    // remove everything from our "good" node from the start of the
    // range to the end of the node. this causes all bad nodes to be
    // severed and auto-closed and auto-opened as necessary at the cut.
    range.setEndAfter(n);
    var clipped = range.extractContents();

    // drop 'el' in after the break (right where we want it)
    n.parentNode.insertBefore(el, n.nextSibling);

    // and re-attach the clipped portion of the "good" node, which
    // includes the auto-opened "bad" nodes.
    el.parentNode.insertBefore(clipped, el.nextSibling);

} else {
    range.insertNode(el);
}

http://jsfiddle.net/zntwL/31/

您的最终解决方案可能需要一些调整。您可能需要以不同的方式检测 #text 节点以兼容跨浏览器。您需要将其模块化并适当地填充badNodes 数组。但是,我认为这是一般的想法。

【讨论】:

  • 虽然这确实确保表格永远不会插入到链接中,但它不会从父段落中转义表格。
  • @BlakePlumb 当然。但是.. 首先,如果不编写完整的合规性检查器,您将无法确保整个文档的完美合规性 - 最好的办法是启发式,如果我可以这样称呼它,它会查找节点链,直到找到允许表的节点.其次,你在这里得到的任何答案可能只会让你开始。一个真正全面的答案是.. large。据我所知,没有“自动”的方式来确保合规性。所以,你需要建立检查。也就是说,我会再玩几分钟,让它变得更好一点。但是,您必须根据自己的喜好“完成”它。
  • @BlakePlumb 我已经编辑了我认为我现在可以接受的答案。祝你好运!
  • 感谢您的帮助!我目前的解决方案与您的第一条评论一致。我想看看是否有人知道如何在当前插入符号位置插入表格,同时保持有效的 html。如果我没有得到更好的解决方案,我会给你赏金。
  • @BlakePlumb 我可能在玩弄小提琴时忽略了真正的需求。我稍后会重新审视这个问题,看看我是否不能更准确地解决这个问题。
【解决方案2】:

类似的东西呢

var parentNode = range.commonAncestorContainer;
parentNode.inertNode(el);

替换你的

range.insertNode(el);

在所有情况下,您可能必须对其进行调整以使表格准确地位于您想要的位置,但至少它永远不会出现在元素的中间。

【讨论】:

  • 感谢您的输入,但我收到以下错误。 TypeError: parentNode.insertNode 不是函数 parentNode.insertNode(el);
  • 嗯,听起来 range.commonAncestorContainer 没有返回 insertNode 可以使用的正确类型的对象。
【解决方案3】:

作为一名 iPhone 应用程序开发人员,我发现以下内容适用于我的可编辑内容

function TableOfContentForSubHeading1() {
    var level = 0;
    document.getElementById("content").innerHTML =
        document.getElementById("content").innerHTML.replace(/<h([\d])>([^<]+)<\/h([\d])>/gi, function (str, openLevel, titleText, closeLevel) {
            if (openLevel != closeLevel) {
                return str;
            }
            if (openLevel > level) {
                toc += (new Array(openLevel - level + 1)).join("<ol>");
            } else if (openLevel < level) {
                toc += (new Array(level - openLevel + 1)).join("</ol>");
            }
            level = parseInt(openLevel);
            var anchor = titleText.replace(/ /g, "_");
            toc += "<li><a href=\"#" + anchor + "\">" + titleText + "</a></li>";
            return "<h" + openLevel + "><a name=\"" + anchor + "\">" + titleText + "</a></h" + closeLevel + ">";
        });
    if (level) {
        toc += (new Array(level + 1)).join("</ol>");
    }
    document.getElementById("content").innerHTML += toc;
}

希望它有效。

【讨论】:

    【解决方案4】:

    如果标题还包含 CLASS 引用或 ID,我在下面做了一些改进。

    <script>
    window.onload = function () {
    var toc = "";
    var level = 0;
    
    document.getElementById("contents").innerHTML =
        document.getElementById("contents").innerHTML.replace(/<h([\d])([^<]+)>([^<]+)<\/h([\d])>/gi,
            function (str, openLevel, classHeading, titleText, closeLevel) {
                if (openLevel != closeLevel) {
                    return str;
                }
    
                if (openLevel > level) {
                    toc += (new Array(openLevel - level + 1)).join("<ul>");
                } else if (openLevel < level) {
                    toc += (new Array(level - openLevel + 1)).join("</ul>");
                }
    
                level = parseInt(openLevel);
    
                var anchor = titleText.replace(/ /g, "_");
                toc += "<li><a href=\"#" + anchor + "\">" + titleText
                    + "</a></li>";
    
                return "<h" + openLevel + classHeading +"><a name=\"" + anchor + "\">"
                    + titleText + "</a></h" + closeLevel + ">";
            }
        );
    
    if (level) {
        toc += (new Array(level + 1)).join("</ul>");
    }
    
    document.getElementById("toc").innerHTML += toc;
    };
    </script>
    

    我在替换函数中添加了一个附加参数:

    ([^<]+)
    

    命名classHeading并将其添加到返回函数中

    return "<h" + openLevel + classHeading + ....
    

    这样就保留了用于的类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-24
      • 1970-01-01
      • 2011-10-07
      • 2017-09-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多