如何避免从 jQuery 中的 contenteditable <p> 中删除键入的文本

Posted

技术标签:

【中文标题】如何避免从 jQuery 中的 contenteditable <p> 中删除键入的文本【英文标题】:How to avoid removing typed text from contenteditable <p> in jQuery 【发布时间】:2020-02-19 23:34:25 【问题描述】:

我正在使用 jQuery UI 可拖动组件将&lt;span&gt; 添加到可编辑的内容&lt;p&gt;

预期的输出是,段落&lt;p&gt; 应该是可编辑的,可拖动组件应该能够拖放到段落,并且&lt;p&gt; 的内容应该是可编辑的。我的代码有问题。当我在&lt;p&gt; 中输入内容并单击外部&lt;p&gt; 时。从段落中删除的输入单词。

我的代码如下:

$(function() 
  function textWrapper(str, sp, btn) 
    if (sp == undefined) 
      sp = [0, 0];
    
    var txt = "";
    if (btn) 
      txt = "<span class='w b'>" + str + "</span>";
     else 
      txt = "<span class='w'>" + str + "</span>";
    

    if (sp[0]) 
      txt = "&nbsp;" + txt;
    

    if (sp[1]) 
      txt = txt + "&nbsp;";
    

    return txt;
  

  function chunkWords(p) 
    var words = p.split(" ");
    words[0] = textWrapper(words[0], [0, 1]);
    var i;
    for (i = 1; i < words.length; i++) 
      var re = /\[.+\]/;
      if (re.test(words[i])) 
        var b = makeTextBox(words[i].slice(1, -1));
        words[i] = "&nbsp;" + b.prop("outerhtml") + "&nbsp;";
       else 
        if (words[0].indexOf(".")) 
          words[i] = textWrapper(words[i], [1, 0]);
         else 
          words[i] = textWrapper(words[i], [1, 1]);
        
      
    
    return words.join("");
  

  function unChunkWords(tObj) 
    var words = [];
    $(".w", tObj).each(function(i, el) 
      console.log($(el).text(), $(el).attr("class"));
      if ($(el).hasClass("b")) 
        words.push("[" + $(el).text().trim() + "]");
       else 
        words.push($(el).text().trim());
      
    );
    return words.join(" ");
  

  function makeBtn(tObj) 
    var btn = $("<span>", 
      class: "ui-icon ui-icon-close"
    ).appendTo(tObj);
  

  function makeTextBox(txt) 
    var sp = $("<span>", 
      class: "w b"
    ).html(txt);
    makeBtn(sp);
    return sp;
  

  function makeDropText(obj) 
    return obj.droppable(
      drop: function(e, ui) 
        var txt = ui.draggable.text();
        var newSpan = textWrapper(txt, [1, 0], 1);
        $(this).after(newSpan);
        makeBtn($(this).next("span.w"));
        makeDropText($(this).next("span.w"));
        $("span.w.ui-state-highlight").removeClass("ui-state-highlight");
      ,
      over: function(e, ui) 
        $(this).add($(this).next("span.w")).addClass("ui-state-highlight");
      ,
      out: function() 
        $(this).add($(this).next("span.w")).removeClass("ui-state-highlight");
      
    );
  

  $("p.given").html(chunkWords($("p.given").text()));

  $("p.given").on("click", ".b > .ui-icon", function() 
    $(this).parent().remove();
  );

  $("p.given").blur(function() 
    var w = unChunkWords($(this));
    console.log(w);
    $(this).html(chunkWords(w));
    makeDropText($("p.given span.w"));
  );

  $("span.given").draggable(
    helper: "clone",
    revert: "invalid"
  );

  makeDropText($("p.given span.w"));
);
p.given 
  display: flex;
  flex-wrap: wrap;


p.given span.w span.ui-icon 
  cursor: pointer;


div.blanks 
  display: inline-block;
  min-width: 50px;
  border-bottom: 2px solid #000000;
  color: #000000;


div.blanks.ui-droppable-active 
  min-height: 20px;


span.answers>b 
  border-bottom: 2px solid #000000;


span.given 
  margin: 5px;
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="row">
  <p class="given" contenteditable="true">Lorem Ipsum is simply dummy text of the printing and typesetting industry. [Lorem] Ipsum has been the industry's standard dummy text ever since the 1500s, Lorem Ipsum is simply dummy text of the printing and typesetting industry.</p>
</div>

<div class="divider"></div>
<div class="section">
  <section>
    <div class="card blue-grey ">
      <div class="card-content white-text">
        <div class="row">
          <div class="col s12">
            <span class="given btn-flat white-text red lighten-1" rel="1">the Santee, thDakota</span>
            <span class="given btn-flat white-text red lighten-1" rel="2">America</span>
            <span class="given btn-flat white-text red lighten-1" rel="3">Qatar</span>
            <span class="given btn-flat white-text red lighten-1" rel="4">Philippines</span>
          </div>
        </div>
      </div>
    </div>
  </section>
</div>

问题间歇性发生。

【问题讨论】:

【参考方案1】:

有时用户输入的文本会再次被删除,原因在于函数unChunkWords

此函数仅迭代 元素(使用类“w”),但不会迭代可能出现在这些元素之间的纯文本节点。而在内容可编辑的元素中,用户确实可以在元素之间的区域中键入文本。所以unChunkWords 中的这个循环永远不会访问这样的文本,在它返回的数组中省略它。

您可以通过将光标放在单词末尾、空格前,然后按右箭头键来强制执行此操作。这会将光标移动到下一个单词的开头,或者它没有明显移动(它只是移出它所在的span)。无论哪种方式,您的光标现在都位于分隔两个单词的文本节点中。键入一些内容并单击其他位置。 ...异常发生了。

有很多方法可以规避这一点。其中之一是使用 jQuery contents() 方法,该方法也收集文本节点。更改以下代码:

$(".w", tObj).each(function(i, el) 
  if ($(el).hasClass("b")) 
    words.push("[" + $(el).text().trim() + "]");
   else 
    words.push($(el).text().trim());
  
);

...到这个:

$(tObj).contents().each(function (i, el) 
  if (el.nodeType !== 3 && !$(el).is(".w")) return; // Only regard ".w" or text nodes
  if ($(el).hasClass("b")) 
    words.push("[" + $(el).text().trim() + "]");
   else 
    words.push($(el).text().trim());
  
);

现在words 中不会省略您输入的文本,即使您在作为内容可编辑元素的直接子节点的文本节点中键入它也是如此。

添加空格

您的代码在 .join(" ") 中添加空格,但未验证文本片段是否真的被 p 元素的内容中的空格分隔。所以,我会抓住 all 内容,包括间距并将其连接起来。这样,您将拥有与 p 元素中完全相同的单词分隔符。

那么你的功能是:

  function unChunkWords(tObj) 
    var words = "";
    $(tObj).contents().each(function (i, el) 
      if ($(el).hasClass("b")) 
        words += "[" + $(el).text() + "]";
       else 
        words += $(el).text();
      
    );
    return words.replace(/\s+/g, " ").trim();
  

免责声明:我只查看了这个特殊问题,指出您为什么会有这种特殊行为,以及如何解决它。这并不意味着现在您的代码将在其所有预期功能中正常工作,这超出了问题的范围。

【讨论】:

@rincot 谢谢先生,它解决了,但是当单击&lt;p&gt; 并单击每个单词之间的&lt;p&gt; 空格时。如何避免? 还有一件事,我想改变可拖动组件的颜色当拖动时间怎么做? 以及为什么 Internet Explorer 会显示这种外观。当鼠标悬停在 &lt;p&gt; i.imgur.com/Vo2Btk9.png 时,它会显示全滚动 这些都是不同的问题。您能否发布一个新问题,并只关注一个问题?当你这样做时,我很乐意看看它/它们。 但是先生的第一个问题 (when click the &lt;p&gt; and click the outside of the &lt;p&gt; whitespaces making between each words) 之前已经解决了。 :) 你能帮我解决这个问题吗?其他两个问题我将单独发布。感谢您的支持和文字:)

以上是关于如何避免从 jQuery 中的 contenteditable <p> 中删除键入的文本的主要内容,如果未能解决你的问题,请参考以下文章

如何从 jQuery 选择器中排除 $(this)?

如何在jquery mobile中更改页面时避免黑屏

如何避免 jquery ajax 中使用 wcf 服务的跨域策略?

如何避免在jQuery中重复类似的代码?

如何通过JQuery实现子窗口操作父窗口

使用 jQuery 从 DOM 元素创建 json