用纯jQuery替换contenteditable中的动态文本

Replacing text on-the-fly in contenteditable with pure jQuery

本文关键字:动态 文本 contenteditable jQuery 替换 用纯      更新时间:2023-09-26

我在StackOverflow上看到了一些关于这方面的问题,但似乎很难找到基于jQuery的解决方案。因此,我想提出这个问题。

我想用属性contenteditable="true"替换div中的动态文本。

我正在寻找一个基于jQuery的解决方案,它将执行以下操作:

  • 动态自动替换书面文本(键入时)
  • 能够继续写作(在替换过程中)

我看了SC Editor(http://www.sceditor.com/),它似乎正是这样做的(例如,如果你尝试键入:),它会被一个表情符号所取代。

我认为一个好的开始应该是一个包含所有元素的数组:

$.settings = {
    path: 'https://example.com/images/',
    emoticons: {
        ':(' : 'stupid.jpg',
        ':)' : 'smart.jpg',
    }
}

我一直找不到这样的好例子。如果有人能分享他们的想法和任何关于这方面的代码,我会很高兴。

使用上述代码,如何以最佳方式进行更换?

我发现了这个。如果你调整一下,它可能会适合你的需要。它将{替换为{}和(替换为(),光标最终位于在中间。

    <script type="text/javascript">
        $(document).ready(function () {
            $("#d").keypress(function (e) {
 
       
                    var charTyped = String.fromCharCode(e.which);
                    if (charTyped == "{" || charTyped == "(") {
                        // Handle this case ourselves
                        e.preventDefault();
                        var sel = window.getSelection();
                        if (sel.rangeCount > 0) {
                            // First, delete the existing selection
                            var range = sel.getRangeAt(0);
                            range.deleteContents();
                            // Insert a text node with the braces/parens
                            var text = (charTyped == "{") ? "{}" : "()";
                            var textNode = document.createTextNode(text);
                            range.insertNode(textNode);
                            // Move the selection to the middle of the text node
                            range.setStart(textNode, 1);
                            range.setEnd(textNode, 1);
                            sel.removeAllRanges();
                            sel.addRange(range);
                        }
                    }
         
            });
        });
    </script>
</head>
<body>
    <div id="d" contentEditable="true">....</div>
</body>
</html>
$('div').keyup(function(){
    //make here for loop which replace all emoticons
    $(this).text().replace(':(', 'stupid.jpg');
});

发布我在找不到这个问题的答案后最终写的内容。我希望这将有助于其他人谁来到这个问题寻求答案(:

我将发布一个更通用的查找和替换解决方案(包含在一个类中)。这适用于内容可编辑的div,在用户键入时有效,此外,它不会影响插入符号的位置。这个实现使用了不区分大小写的搜索(尽管在代码中禁用它是微不足道的)。它的另一个优点是,即使您在段落的中间(而不仅仅是在行的末尾)打字,它也可以工作,并且可以处理粘贴的文本。试试看!

class FindAndReplace {
	constructor($contentEditable, findAndReplaceData) {
		var self = this;
		$contentEditable.on('input blur', function () {
			var textNodes = self.getTextNodes($contentEditable);
			textNodes.each(function (i) {
				// Perform all replacements on text
				findAndReplaceData.forEach(function (findAndReplaceDatum) {
					var find = findAndReplaceDatum.find;
					var replace = findAndReplaceDatum.replace;
					var regexEscapedFind = self.escapeRegExp(find);
					var regexEscapedReplace = self.escapeRegExp(replace);
					var regexEscapedCaseInsensitiveFind = self.makeRegexCaseInsensitive(regexEscapedFind);
					// Case insensitive search for the find with a negative lookahead to check its not a case sensitive match of the replacement (aka to check its actually going to make a difference)
					var regexString = `(?!${regexEscapedReplace})${regexEscapedCaseInsensitiveFind}`;
					do {
						// Get the latest version of the text node
						textNode = self.getTextNodes($contentEditable)[i];
						var text = textNode.data;
						var regex = new RegExp(regexString);
						var matchIndex = text.search(regex);
						var matchFound = (matchIndex !== -1);
						if (matchFound) {
							// Select the match
							var range = document.createRange();
							range.setStart(textNode, matchIndex);
							range.setEnd(textNode, matchIndex + find.length);
							// Delete it
							range.deleteContents();
							// Create the replacement node
							var textNode = document.createTextNode(replace);
							// Insert it
							range.insertNode(textNode);
							// Set the range to the end of the selected node
							range.collapse(false);
							// Set the user selection the range
							var sel = window.getSelection();
							sel.removeAllRanges();
							sel.addRange(range);
							// Make sure there a no adjacent or empty text nodes
							$contentEditable[0].normalize();
						}
					} while (matchFound)
				});
			});
		});
	}
	escapeRegExp(string) {
		// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
		return string.replace(/[.*+?^${}()|[']'']/g, '''$&'); // $& means the whole matched string
	}
	getTextNodes($contentEditable) {
		return $contentEditable.contents().filter(function () {
			return this.nodeType == 3; // Text node
		});
	}
	makeRegexCaseInsensitive(string) {
		var stringArray = string.split('');
		stringArray = stringArray.map(function (char) {
			if (char.toLowerCase() !== char.toUpperCase())
				return '[' + char.toLowerCase() + char.toUpperCase() + ']';
			else
				return char;
		});
		return stringArray.join('');
	}
}
div {
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(function(){
  var findAndReplaceData = [
	  {
      'find': 'find me',
      'replace': 'and replace with me!'
    },
    {
      'find': 'foo',
      'replace': 'bar'
    },
    {
      'find': 'no',
      'replace': 'yes'
    }
  ];
  $contentEditable = $('div');
  new FindAndReplace($contentEditable,findAndReplaceData);
});
</script>
<div contenteditable="true"></div>