简体   繁体   English

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

[英]How to avoid removing typed text from contenteditable <p> in jQuery

I'm using jQuery UI draggable component to add <span> to content editable <p> .我正在使用 jQuery UI 可拖动组件将<span>添加到内容可编辑<p>

The expected output was, paragraph <p> should be editable and the draggable component should able to drag and drop to paragraph and also the content of the <p> should able to be editable.预期的 output 是,段落<p>应该是可编辑的,可拖动组件应该能够拖放到段落,并且<p>的内容应该能够是可编辑的。 I have problems with my code.我的代码有问题。 When I type something inside <p> and click the outside <p> .当我在<p>内输入内容并单击外部<p>时。 typed words removing from the paragraph.从段落中删除的输入单词。

My code as follows:我的代码如下:

 $(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: "wb" }).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>

The problem occurs intermittently.问题间歇性发生。

Sometimes the user-entered text is removed again, and the cause is in the function unChunkWords :有时用户输入的文本再次被删除,原因在 function unChunkWords

This function iterates only over elements (with class "w"), but it does not iterate over the plain text nodes that may occur in-between those elements.此 function 仅迭代元素(使用 class “w”),但不会迭代可能出现在这些元素之间的纯文本节点。 And in a content-editable element, the user can indeed type text in areas between elements.在内容可编辑的元素中,用户确实可以在元素之间的区域中键入文本。 And so this loop in unChunkWords will never visit such text, omitting it in the array it returns.所以unChunkWords中的这个循环永远不会访问这样的文本,在它返回的数组中省略它。

You can force it to happen by putting the cursor at the end of a word, before a space, then press the right arrow key.您可以通过将 cursor 放在单词末尾、空格前,然后按右箭头键来强制它发生。 Either this moves the cursor to the start of the next word, or it does not move visibly (it just moved out of the span it was in).这会将 cursor 移动到下一个单词的开头,或者它没有明显移动(它只是移出它所在的span )。 Either way, your cursor is now in the text node that separates the two words.无论哪种方式,您的 cursor 现在位于分隔两个单词的文本节点中。 Type something and click somewhere else.键入一些内容并单击其他位置。 ... the anomaly happens. ... 异常发生。

There are many ways to circumvent this.有很多方法可以规避这一点。 One of them is to use the jQuery contents() method, which also collects text nodes.其中之一是使用 jQuery contents()方法,该方法也收集文本节点。 Change the following code:更改以下代码:

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

...to this: ...对此:

$(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());
  }
});

Now the text you enter will not be omitted from words , even when you type it in text nodes that are direct children of the content-editable element.现在,您输入的文本不会从words中省略,即使您在作为 content-editable 元素的直接子节点的文本节点中键入它也是如此。

Adding spaces添加空格

Your code is adding spaces with .join(" ") without verifying that the text fragments are really separated by white space in the content of the p element.您的代码使用.join(" ")添加空格,但未验证文本片段是否真的被p元素内容中的空格分隔。 So, I would just grab all content, including spacing and just concatenate that.因此,我将抓取所有内容,包括间距并将其连接起来。 That way you will have the word separations exactly as they are in the p element.这样,您将拥有与p元素中完全相同的单词分离。

So then your function would be:那么你的 function 将是:

  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();
  }

Disclaimer: I have only looked at this particular problem, pointing out why you have this particular behaviour, and how to fix it.免责声明:我只查看了这个特定的问题,指出了为什么你有这种特殊的行为,以及如何解决它。 This does not mean that now your code will work correctly in all its intended functionality, which would go beyond the scope of the question.这并不意味着现在您的代码将在其所有预期功能中正常工作,这将使 go 超出问题的 scope。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM