I'm currently trying to write a JS function to provide a "copy link on button click". It works fine on Android and PC but when I try it on my iPad or iPhone I'm getting an error:
TypeError: Argument 1 ('refNode') to Range.selectNodeContents must be
an instance of Node
I've build in a way to copy it on IOS devices too, because the normal copy command don't works:
function copyUrl(e) {
var tmp = jQuery("<input>");
jQuery("body").append(tmp.val(e));
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
var editable = tmp.contentEditable;
var readOnly = tmp.readOnly;
tmp.contentEditable = true;
tmp.readOnly = false;
var range = document.createRange();
range.selectNodeContents(tmp);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
tmp.setSelectionRange(0, 999999);
tmp.contentEditable = editable;
tmp.readOnly = readOnly;
} else {
tmp.select();
}
document.execCommand("copy");
tmp.remove();
alert("Link copied successfully!")
}
div {
padding: 30px;
}
a {
border: 1px solid;
padding: 12px 10px;
cursor: pointer;
}
a:hover {
border-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<a class="btn-floating" onclick="copyUrl('google.de')">Share</a>
</div>
What have I missed?
If you pass the JQuery element as an argument element, it will give
the TypeError you are getting because it does not interface the Node.
The TypeError message is related to you not doing one of the following.
// copy(document.getElementByClass("")[0];
copy(document.getElementById("")); // Pure JS
copy($("#")[0]); // JQuery
Example, as asked for a link passing a variable string: It creates an element then removes it after selecting it and copying the variable's value we inserted in it.
I suggest looking into the library Cliboard.js
function copy(href) {
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.setAttribute('value', href);
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}
Copy
FROM IOS Copy to clipboard using Javascript in iOS
var copy = function(href) {
var input = document.createElement("input");
document.body.appendChild(input);
input.setAttribute('value', href);
var isiOSDevice = navigator.userAgent.match(/ipad|iphone/i);
if (isiOSDevice) {
var editable = input.contentEditable;
var readOnly = input.readOnly;
input.contentEditable = true;
input.readOnly = false;
var range = document.createRange();
range.selectNodeContents(input);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
input.setSelectionRange(0, 999999);
input.contentEditable = editable;
input.readOnly = readOnly;
document.body.removeChild(input);
} else {
input.select();
}
document.execCommand('copy');
document.body.removeChild(input);
}
I think this works on ios
Copy text
Try something like this:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$('.copy-to-clipboard input').click(function() {
$(this).focus();
$(this).select();
document.execCommand('copy');
alert('Copy to Clipboard!');
});
});
</script>
<span class="copy-to-clipboard">
<input id="copy-test" readonly type="text" value="google.de">
</span>
Try this one
/*
Copy text from any appropriate field to the clipboard
By Craig Buckler, #craigbuckler
use it, abuse it, do whatever you like with it!
*/
(function() {
'use strict';
// click events
document.body.addEventListener('click', copy, true);
// event handler
function copy(e) {
// find target element
var
t = e.target,
c = t.dataset.copytarget,
inp = (c ? document.querySelector(c) : null);
// is element selectable?
if (inp && inp.select) {
// select text
inp.select();
try {
// copy text
document.execCommand('copy');
inp.blur();
// copied animation
t.classList.add('copied');
setTimeout(function() { t.classList.remove('copied'); }, 1500);
}
catch (err) {
alert('please press Ctrl/Cmd+C to copy');
}
}
}
})();
div {
padding: 30px;
}
a {
border: 1px solid;
padding: 12px 10px;
cursor: pointer;
}
a:hover {
border-color: blue;
}
<div>
<input style="position: absolute;
height: 100px;
width: 100px;
right: -150px;
top: -150px;overflow:hidden;" type="text" id="website" value="http://hpecas.com/teste" />
<a class="btn-floating" href="#" data-copytarget="#website">Share</a>
</div>
Related
hello I'm trying to create syntax highlight in my web texteditor and my function is working and not at the same time I don't know why
when I type the word:
but in the editor:
this is the code:
const textarea = document.getElementById('editor');
updateText(textarea.value);
textarea.addEventListener("keydown", function(e) {
setTimeout(() =>{
updateText(textarea.value);
},0)
})
function updateText(text)
{
textarea.innerHTML = colorize(text.replace(/\n/g, "<br>").replace(/\t/g," "));
}
function colorize(text)
{
var words = ["int","class","#include","namespace"];
for(const keyword of words)
{
text = text.replaceAll(keyword,`<span style="color:#569cd6">${keyword}</span>`)
text = text.replaceAll(keyword.toLowerCase(),`<span style="color:#569cd6">${keyword.toLowerCase()}</span>`)
}
return text
}
I tried to make it change color
So here's what I have tried following your approach, it still needs improvement, thou:
const textarea = document.getElementById("editor");
function moveCursorAtTheEnd() {
var selection = document.getSelection();
var range = document.createRange();
var contenteditable = document.querySelector('div[contenteditable="true"]');
if (contenteditable.lastChild.nodeType == 3) {
range.setStart(contenteditable.lastChild, contenteditable.lastChild.length);
} else {
range.setStart(contenteditable, contenteditable.childNodes.length);
}
selection.removeAllRanges();
selection.addRange(range);
}
textarea.addEventListener("keydown", function (e) {
if (e.keyCode == 32 || e.keyCode == 13) updateText(textarea.textContent);
textarea.innerHTML = textarea.innerHTML + " ";
moveCursorAtTheEnd();
});
function updateText(text) {
textarea.innerHTML = colorize(
text.replace(/\n/g, "<br>").replace(/\t/g, " ")
);
}
function colorize(text) {
var words = ["int", "class", "#include", "namespace"];
for (const keyword of words) {
text = text.replaceAll(
keyword,
`<span style="color:#569cd6">${keyword}</span>`
);
text = text.replaceAll(
keyword.toLowerCase(),
`<span style="color:#569cd6">${keyword.toLowerCase()}</span>`
);
}
return text;
}
#editor {
background: lightgrey;
height: 100vh;
}
[contenteditable]:focus {
outline: 0px solid transparent;
}
<div contentEditable="true" id="editor" onfocus="this.value = this.value;"></div>
After all, for a robust editor, I'd recommend you to use ACE editor, you can have a look at the live demo here: https://ace.c9.io/build/kitchen-sink.html It's not that difficult to implement. Hope it helps!
You can't do this inside a , not one that's editable. It sounds like you're after a WYSIWYG editor, most of which use a to do this.
There are several JavaScript options for this, to name a few:
TinyMCE
CKEditor
I have simple javascript rich text editor consiting only of bold button that has following onclick:
document.execCommand('bold', false)
And simple html...
<div contenteditable="true">
My problem is that when I click the bold button, text area loses it's focus, is there some solution for that?
Well the focus moves to the button so you need to cancel the click action so the focus is not lost in the content editable element.
document.querySelector(".actions").addEventListener("mousedown", function (e) {
var action = e.target.dataset.action;
if (action) {
document.execCommand(action, false)
//prevent button from actually getting focused
e.preventDefault();
}
})
[contenteditable] {
width: 300px;
height: 300px;
border: 1px solid black;
}
<div class="actions">
<button data-action="bold">bold</button>
<button data-action="italic">italic</button>
</div>
<div contenteditable="true"></div>
ANSWER UPDATE
Taking a look to this answer you can save and restore current contenteditable position adding the blur event listener to your contenteditable
An example:
//
// restore position after click
//
document.getElementById('btn').addEventListener('click', function(e) {
restoreSelection(cpos);
})
//
// save position on blur
//
document.querySelector('div[contenteditable="true"]').addEventListener('blur', function(e) {
cpos = saveSelection();
})
function saveSelection() {
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
}
function restoreSelection(range) {
if (range) {
if (window.getSelection) {
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && range.select) {
range.select();
}
}
}
var cpos = -1;
<button id="btn">Make bold</button>
<div contenteditable="true">
this is the text
</div>
I have a basic editor based on execCommand following the sample introduced here. There are three ways to paste text within the execCommand area:
Ctrl+V
Right Click -> Paste
Right Click -> Paste As Plain Text
I want to allow pasting only plain text without any HTML markup. How can I force the first two actions to paste Plain Text?
Possible Solution: The way I can think of is to set listener for keyup events for (Ctrl+V) and strip HTML tags before paste.
Is it the best solution?
Is it bulletproof to avoid any HTML markup in paste?
How to add listener to Right Click -> Paste?
It will intercept the paste event, cancel the paste, and manually insert the text representation of the clipboard:
http://jsfiddle.net/HBEzc/.
This should be the most reliable:
It catches all kinds of pasting (Ctrl+V, context menu, etc.)
It allows you to get the clipboard data directly as text, so you don't have to do ugly hacks to replace HTML.
I'm not sure of cross-browser support, though.
editor.addEventListener("paste", function(e) {
// cancel paste
e.preventDefault();
// get text representation of clipboard
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
// insert text manually
document.execCommand("insertHTML", false, text);
});
I couldn't get the accepted answer here to work in IE so I did some scouting around and came to this answer which works in IE11 and the latest versions of Chrome and Firefox.
$('[contenteditable]').on('paste', function(e) {
e.preventDefault();
var text = '';
if (e.clipboardData || e.originalEvent.clipboardData) {
text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
text = window.clipboardData.getData('Text');
}
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertText', false, text);
} else {
document.execCommand('paste', false, text);
}
});
A close solution as pimvdb. But it's working of FF, Chrome and IE 9:
editor.addEventListener("paste", function(e) {
e.preventDefault();
if (e.clipboardData) {
content = (e.originalEvent || e).clipboardData.getData('text/plain');
document.execCommand('insertText', false, content);
}
else if (window.clipboardData) {
content = window.clipboardData.getData('Text');
document.selection.createRange().pasteHTML(content);
}
});
Of course the question is already answered and the topic very old but I want to provide my solution as it is simple an clean:
This is inside my paste-event on my contenteditable-div.
var text = '';
var that = $(this);
if (e.clipboardData)
text = e.clipboardData.getData('text/plain');
else if (window.clipboardData)
text = window.clipboardData.getData('Text');
else if (e.originalEvent.clipboardData)
text = $('<div></div>').text(e.originalEvent.clipboardData.getData('text'));
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertHTML', false, $(text).html());
return false;
}
else { // IE > 7
that.find('*').each(function () {
$(this).addClass('within');
});
setTimeout(function () {
// nochmal alle durchlaufen
that.find('*').each(function () {
// wenn das element keine klasse 'within' hat, dann unwrap
// http://api.jquery.com/unwrap/
$(this).not('.within').contents().unwrap();
});
}, 1);
}
The else-part is from another SO-post I couldn't find anymore...
UPDATE 19.11.2014:
The other SO-post
None of the posted answers really seems to work cross browser or the solution is over complicated:
The command insertText is not supported by IE
Using the paste command results in stack overflow error in IE11
What worked for me (IE11, Edge, Chrome and FF) is the following:
$("div[contenteditable=true]").off('paste').on('paste', function(e) {
e.preventDefault();
var text = e.originalEvent.clipboardData ? e.originalEvent.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');
_insertText(text);
});
function _insertText(text) {
// use insertText command if supported
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertText', false, text);
}
// or insert the text content at the caret's current position
// replacing eventually selected content
else {
var range = document.getSelection().getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
range.selectNodeContents(textNode);
range.collapse(false);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<textarea name="t1"></textarea>
<div style="border: 1px solid;" contenteditable="true">Edit me!</div>
<input />
</body>
Note that the custom paste handler is only needed/working for contenteditable nodes. As both textarea and plain input fields don't support pasting HTML content at all, so nothing needs to be done here.
Note that execCommand() is deprecated, so avoid using it in your production environments. Do something like this instead:
const editor = document.querySelector('[contentEditable]')
editor.addEventListener('paste', handlePaste)
function handlePaste(e) {
e.preventDefault()
const text = (e.clipboardData || window.clipboardData).getData('text')
const selection = window.getSelection()
if (selection.rangeCount) {
selection.deleteFromDocument()
selection.getRangeAt(0).insertNode(document.createTextNode(text))
}
}
[contentEditable] {
padding: 1em;
border: 1px solid black;
}
<div contentEditable>Paste HTML here</div>
References:
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event
Firefox does not allow you to access the clipboard data so you'll need to make a 'hack' to get it to work. I've not been able to find a complete solution, however you can fix it for ctrl+v pastes by creating a textarea & pasting to that instead:
//Test if browser has the clipboard object
if (!window.Clipboard)
{
/*Create a text area element to hold your pasted text
Textarea is a good choice as it will make anything added to it in to plain text*/
var paster = document.createElement("textarea");
//Hide the textarea
paster.style.display = "none";
document.body.appendChild(paster);
//Add a new keydown event tou your editor
editor.addEventListener("keydown", function(e){
function handlePaste()
{
//Get the text from the textarea
var pastedText = paster.value;
//Move the cursor back to the editor
editor.focus();
//Check that there is a value. FF throws an error for insertHTML with an empty string
if (pastedText !== "") document.execCommand("insertHTML", false, pastedText);
//Reset the textarea
paster.value = "";
}
if (e.which === 86 && e.ctrlKey)
{
//ctrl+v => paste
//Set the focus on your textarea
paster.focus();
//We need to wait a bit, otherwise FF will still try to paste in the editor => settimeout
window.setTimeout(handlePaste, 1);
}
}, false);
}
else //Pretty much the answer given by pimvdb above
{
//Add listener for paster to force paste-as-plain-text
editor.addEventListener("paste", function(e){
//Get the plain text from the clipboard
var plain = (!!e.clipboardData)? e.clipboardData.getData("text/plain") : window.clipboardData.getData("Text");
//Stop default paste action
e.preventDefault();
//Paste plain text
document.execCommand("insertHTML", false, plain);
}, false);
}
I was also working on a plain text paste and I started to hate all the execCommand and getData errors, so I decided to do it the classic way and it works like a charm:
$('#editor').bind('paste', function(){
var before = document.getElementById('editor').innerHTML;
setTimeout(function(){
var after = document.getElementById('editor').innerHTML;
var pos1 = -1;
var pos2 = -1;
for (var i=0; i<after.length; i++) {
if (pos1 == -1 && before.substr(i, 1) != after.substr(i, 1)) pos1 = i;
if (pos2 == -1 && before.substr(before.length-i-1, 1) != after.substr(after.length-i-1, 1)) pos2 = i;
}
var pasted = after.substr(pos1, after.length-pos2-pos1);
var replace = pasted.replace(/<[^>]+>/g, '');
var replaced = after.substr(0, pos1)+replace+after.substr(pos1+pasted.length);
document.getElementById('editor').innerHTML = replaced;
}, 100);
});
The code with my notations can be found here:
http://www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript
function PasteString() {
var editor = document.getElementById("TemplateSubPage");
editor.focus();
// editor.select();
document.execCommand('Paste');
}
function CopyString() {
var input = document.getElementById("TemplateSubPage");
input.focus();
// input.select();
document.execCommand('Copy');
if (document.selection || document.textSelection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
}
Above code works for me in IE10 and IE11 and now also works in Chrome and Safari. Not tested in Firefox.
In IE11, execCommand doesn't work well. I use below code for IE11
<div class="wmd-input" id="wmd-input-md" contenteditable=true>
is my div box.
I read clipboard data from window.clipboardData and modify div's textContent and give caret.
I give timeout for setting caret, because if I don't set timeout, a caret goes to end of div.
and you should read clipboardData in IE11 in below way. If you don't do it, newline caracter is not handled properly, so caret goes wrong.
var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;
Tested on IE11 and chrome. It may not work on IE9
document.getElementById("wmd-input-md").addEventListener("paste", function (e) {
if (!e.clipboardData) {
//For IE11
e.preventDefault();
e.stopPropagation();
var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;
var selection = document.getSelection();
var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;
selection.removeAllRanges();
setTimeout(function () {
$(".wmd-input").text($(".wmd-input").text().substring(0, start)
+ text
+ $(".wmd-input").text().substring(end));
var range = document.createRange();
range.setStart(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);
range.setEnd(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);
selection.addRange(range);
}, 1);
} else {
//For Chrome
e.preventDefault();
var text = e.clipboardData.getData("text");
var selection = document.getSelection();
var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;
$(this).text($(this).text().substring(0, start)
+ text
+ $(this).text().substring(end));
var range = document.createRange();
range.setStart($(this)[0].firstChild, start + text.length);
range.setEnd($(this)[0].firstChild, start + text.length);
selection.removeAllRanges();
selection.addRange(range);
}
}, false);
After along search and trying I have found somehow kind of optimal solution
what is important to keep in mind
// /\x0D/g return key ASCII
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))
and give the css style white-space: pre-line //for displaying
var contenteditable = document.querySelector('[contenteditable]')
contenteditable.addEventListener('paste', function(e){
let text = ''
contenteditable.classList.remove('empty')
e.preventDefault()
text = (e.originalEvent || e).clipboardData.getData('text/plain')
e.clipboardData.setData('text/plain', '')
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))// /\x0D/g return ASCII
})
#input{
width: 100%;
height: 100px;
border: 1px solid black;
white-space: pre-line;
}
<div id="input"contenteditable="true">
<p>
</p>
</div>
OK as everybody is trying to work around clipboard data, checking keypress event, and using execCommand.
I thought of this
CODE
handlePastEvent=()=>{
document.querySelector("#new-task-content-1").addEventListener("paste",function(e)
{
setTimeout(function(){
document.querySelector("#new-task-content-1").innerHTML=document.querySelector("#new-task-content-1").innerText.trim();
},1);
});
}
handlePastEvent();
<div contenteditable="true" id="new-task-content-1">You cann't paste HTML here</div>
In 2022, you can use CSS user-modify: read-write-plaintext-only to archive this
.plain-text-only {
user-modify: read-write-plaintext-only;
-moz-user-modify: read-write-plaintext-only;
-webkit-user-modify: read-write-plaintext-only;
}
div[contenteditable] {
padding: 1rem 0.5rem;
border: 2px solid #eee;
border-radius: 4px;
margin-bottom: 2rem;
}
<div contenteditable class="plain-text-only">Can't paste HTML here</div>
<div contenteditable>HTML styled text free</div>
I have a basic editor based on execCommand following the sample introduced here. There are three ways to paste text within the execCommand area:
Ctrl+V
Right Click -> Paste
Right Click -> Paste As Plain Text
I want to allow pasting only plain text without any HTML markup. How can I force the first two actions to paste Plain Text?
Possible Solution: The way I can think of is to set listener for keyup events for (Ctrl+V) and strip HTML tags before paste.
Is it the best solution?
Is it bulletproof to avoid any HTML markup in paste?
How to add listener to Right Click -> Paste?
It will intercept the paste event, cancel the paste, and manually insert the text representation of the clipboard:
http://jsfiddle.net/HBEzc/.
This should be the most reliable:
It catches all kinds of pasting (Ctrl+V, context menu, etc.)
It allows you to get the clipboard data directly as text, so you don't have to do ugly hacks to replace HTML.
I'm not sure of cross-browser support, though.
editor.addEventListener("paste", function(e) {
// cancel paste
e.preventDefault();
// get text representation of clipboard
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
// insert text manually
document.execCommand("insertHTML", false, text);
});
I couldn't get the accepted answer here to work in IE so I did some scouting around and came to this answer which works in IE11 and the latest versions of Chrome and Firefox.
$('[contenteditable]').on('paste', function(e) {
e.preventDefault();
var text = '';
if (e.clipboardData || e.originalEvent.clipboardData) {
text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
text = window.clipboardData.getData('Text');
}
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertText', false, text);
} else {
document.execCommand('paste', false, text);
}
});
A close solution as pimvdb. But it's working of FF, Chrome and IE 9:
editor.addEventListener("paste", function(e) {
e.preventDefault();
if (e.clipboardData) {
content = (e.originalEvent || e).clipboardData.getData('text/plain');
document.execCommand('insertText', false, content);
}
else if (window.clipboardData) {
content = window.clipboardData.getData('Text');
document.selection.createRange().pasteHTML(content);
}
});
Of course the question is already answered and the topic very old but I want to provide my solution as it is simple an clean:
This is inside my paste-event on my contenteditable-div.
var text = '';
var that = $(this);
if (e.clipboardData)
text = e.clipboardData.getData('text/plain');
else if (window.clipboardData)
text = window.clipboardData.getData('Text');
else if (e.originalEvent.clipboardData)
text = $('<div></div>').text(e.originalEvent.clipboardData.getData('text'));
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertHTML', false, $(text).html());
return false;
}
else { // IE > 7
that.find('*').each(function () {
$(this).addClass('within');
});
setTimeout(function () {
// nochmal alle durchlaufen
that.find('*').each(function () {
// wenn das element keine klasse 'within' hat, dann unwrap
// http://api.jquery.com/unwrap/
$(this).not('.within').contents().unwrap();
});
}, 1);
}
The else-part is from another SO-post I couldn't find anymore...
UPDATE 19.11.2014:
The other SO-post
None of the posted answers really seems to work cross browser or the solution is over complicated:
The command insertText is not supported by IE
Using the paste command results in stack overflow error in IE11
What worked for me (IE11, Edge, Chrome and FF) is the following:
$("div[contenteditable=true]").off('paste').on('paste', function(e) {
e.preventDefault();
var text = e.originalEvent.clipboardData ? e.originalEvent.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');
_insertText(text);
});
function _insertText(text) {
// use insertText command if supported
if (document.queryCommandSupported('insertText')) {
document.execCommand('insertText', false, text);
}
// or insert the text content at the caret's current position
// replacing eventually selected content
else {
var range = document.getSelection().getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
range.selectNodeContents(textNode);
range.collapse(false);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<textarea name="t1"></textarea>
<div style="border: 1px solid;" contenteditable="true">Edit me!</div>
<input />
</body>
Note that the custom paste handler is only needed/working for contenteditable nodes. As both textarea and plain input fields don't support pasting HTML content at all, so nothing needs to be done here.
Note that execCommand() is deprecated, so avoid using it in your production environments. Do something like this instead:
const editor = document.querySelector('[contentEditable]')
editor.addEventListener('paste', handlePaste)
function handlePaste(e) {
e.preventDefault()
const text = (e.clipboardData || window.clipboardData).getData('text')
const selection = window.getSelection()
if (selection.rangeCount) {
selection.deleteFromDocument()
selection.getRangeAt(0).insertNode(document.createTextNode(text))
}
}
[contentEditable] {
padding: 1em;
border: 1px solid black;
}
<div contentEditable>Paste HTML here</div>
References:
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event
Firefox does not allow you to access the clipboard data so you'll need to make a 'hack' to get it to work. I've not been able to find a complete solution, however you can fix it for ctrl+v pastes by creating a textarea & pasting to that instead:
//Test if browser has the clipboard object
if (!window.Clipboard)
{
/*Create a text area element to hold your pasted text
Textarea is a good choice as it will make anything added to it in to plain text*/
var paster = document.createElement("textarea");
//Hide the textarea
paster.style.display = "none";
document.body.appendChild(paster);
//Add a new keydown event tou your editor
editor.addEventListener("keydown", function(e){
function handlePaste()
{
//Get the text from the textarea
var pastedText = paster.value;
//Move the cursor back to the editor
editor.focus();
//Check that there is a value. FF throws an error for insertHTML with an empty string
if (pastedText !== "") document.execCommand("insertHTML", false, pastedText);
//Reset the textarea
paster.value = "";
}
if (e.which === 86 && e.ctrlKey)
{
//ctrl+v => paste
//Set the focus on your textarea
paster.focus();
//We need to wait a bit, otherwise FF will still try to paste in the editor => settimeout
window.setTimeout(handlePaste, 1);
}
}, false);
}
else //Pretty much the answer given by pimvdb above
{
//Add listener for paster to force paste-as-plain-text
editor.addEventListener("paste", function(e){
//Get the plain text from the clipboard
var plain = (!!e.clipboardData)? e.clipboardData.getData("text/plain") : window.clipboardData.getData("Text");
//Stop default paste action
e.preventDefault();
//Paste plain text
document.execCommand("insertHTML", false, plain);
}, false);
}
I was also working on a plain text paste and I started to hate all the execCommand and getData errors, so I decided to do it the classic way and it works like a charm:
$('#editor').bind('paste', function(){
var before = document.getElementById('editor').innerHTML;
setTimeout(function(){
var after = document.getElementById('editor').innerHTML;
var pos1 = -1;
var pos2 = -1;
for (var i=0; i<after.length; i++) {
if (pos1 == -1 && before.substr(i, 1) != after.substr(i, 1)) pos1 = i;
if (pos2 == -1 && before.substr(before.length-i-1, 1) != after.substr(after.length-i-1, 1)) pos2 = i;
}
var pasted = after.substr(pos1, after.length-pos2-pos1);
var replace = pasted.replace(/<[^>]+>/g, '');
var replaced = after.substr(0, pos1)+replace+after.substr(pos1+pasted.length);
document.getElementById('editor').innerHTML = replaced;
}, 100);
});
The code with my notations can be found here:
http://www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript
function PasteString() {
var editor = document.getElementById("TemplateSubPage");
editor.focus();
// editor.select();
document.execCommand('Paste');
}
function CopyString() {
var input = document.getElementById("TemplateSubPage");
input.focus();
// input.select();
document.execCommand('Copy');
if (document.selection || document.textSelection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
}
Above code works for me in IE10 and IE11 and now also works in Chrome and Safari. Not tested in Firefox.
In IE11, execCommand doesn't work well. I use below code for IE11
<div class="wmd-input" id="wmd-input-md" contenteditable=true>
is my div box.
I read clipboard data from window.clipboardData and modify div's textContent and give caret.
I give timeout for setting caret, because if I don't set timeout, a caret goes to end of div.
and you should read clipboardData in IE11 in below way. If you don't do it, newline caracter is not handled properly, so caret goes wrong.
var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;
Tested on IE11 and chrome. It may not work on IE9
document.getElementById("wmd-input-md").addEventListener("paste", function (e) {
if (!e.clipboardData) {
//For IE11
e.preventDefault();
e.stopPropagation();
var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;
var selection = document.getSelection();
var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;
selection.removeAllRanges();
setTimeout(function () {
$(".wmd-input").text($(".wmd-input").text().substring(0, start)
+ text
+ $(".wmd-input").text().substring(end));
var range = document.createRange();
range.setStart(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);
range.setEnd(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);
selection.addRange(range);
}, 1);
} else {
//For Chrome
e.preventDefault();
var text = e.clipboardData.getData("text");
var selection = document.getSelection();
var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;
$(this).text($(this).text().substring(0, start)
+ text
+ $(this).text().substring(end));
var range = document.createRange();
range.setStart($(this)[0].firstChild, start + text.length);
range.setEnd($(this)[0].firstChild, start + text.length);
selection.removeAllRanges();
selection.addRange(range);
}
}, false);
After along search and trying I have found somehow kind of optimal solution
what is important to keep in mind
// /\x0D/g return key ASCII
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))
and give the css style white-space: pre-line //for displaying
var contenteditable = document.querySelector('[contenteditable]')
contenteditable.addEventListener('paste', function(e){
let text = ''
contenteditable.classList.remove('empty')
e.preventDefault()
text = (e.originalEvent || e).clipboardData.getData('text/plain')
e.clipboardData.setData('text/plain', '')
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))// /\x0D/g return ASCII
})
#input{
width: 100%;
height: 100px;
border: 1px solid black;
white-space: pre-line;
}
<div id="input"contenteditable="true">
<p>
</p>
</div>
OK as everybody is trying to work around clipboard data, checking keypress event, and using execCommand.
I thought of this
CODE
handlePastEvent=()=>{
document.querySelector("#new-task-content-1").addEventListener("paste",function(e)
{
setTimeout(function(){
document.querySelector("#new-task-content-1").innerHTML=document.querySelector("#new-task-content-1").innerText.trim();
},1);
});
}
handlePastEvent();
<div contenteditable="true" id="new-task-content-1">You cann't paste HTML here</div>
In 2022, you can use CSS user-modify: read-write-plaintext-only to archive this
.plain-text-only {
user-modify: read-write-plaintext-only;
-moz-user-modify: read-write-plaintext-only;
-webkit-user-modify: read-write-plaintext-only;
}
div[contenteditable] {
padding: 1rem 0.5rem;
border: 2px solid #eee;
border-radius: 4px;
margin-bottom: 2rem;
}
<div contenteditable class="plain-text-only">Can't paste HTML here</div>
<div contenteditable>HTML styled text free</div>
I have two <textarea>s. One with id="input" and the other with id="selection".
<textarea id="input"> will contain some HTML. The user will select some text in this textarea, click a button and the selected text will be copied to <textarea id="selection">.
I can use jQuery or just vanilla JavaScript and I'd like it to work in IE7+, Safari and Firefox.
There's only one way I've been able to do it. The problem you're running into, as you may be aware, is that when you click the button (thus firing the event to copy the selection), the textarea loses focus, and thereby there's no text selected.
So as a workaround, I styled a div to look (kinda) like a textarea. That seems to work:
<style type="text/css">
.textarea {
border:1px solid black;
width:200px;
height:100px;
overflow-y: auto;
float:left;
}
</style>
The markup then looks like this:
<div id="input" class="textarea">This is a test</div>
<textarea id="selection"></textarea>
<button id="theButton">Copy</button>
And finally, the script:
var selText = "";
$( document ).ready( function() {
$( '#theButton' ).mousedown( function() {
$( '#selection' ).val( getSelectedText() );
});
});
function getSelectedText(){
if ( window.getSelection ) {
return window.getSelection().toString();
}
else if ( document.getSelection ) {
return document.getSelection();
} else if ( document.selection ) {
return document.selection.createRange().text;
}
}
To give full credit where it's due, I got the getSelectedText() method from http://esbueno.noahstokes.com/post/92274686/highlight-selected-text-with-jquery
The following will do it:
See it in action: http://www.jsfiddle.net/QenBV/1/
function getSelectedText(el) {
if (typeof el.selectionStart == "number") {
return el.value.slice(el.selectionStart, el.selectionEnd);
} else if (typeof document.selection != "undefined") {
var range = document.selection.createRange();
if (range.parentElement() == el) {
return range.text;
}
}
return "";
}
function copySelected() {
var srcTextarea = document.getElementById("input");
var destTextarea = document.getElementById("selection");
destTextarea.value = getSelectedText(srcTextarea);
}
<input type="button" onclick="copySelected()" value="copy selected">
with jquery you can do as below
$('#selection').val($('#input').val());
http://api.jquery.com/val/