comandos - Truco de Javascript para ''pegar como texto sin formato'' en execCommand
comandos de java script (9)
Tengo un editor básico basado en execCommand
siguiendo la muestra presentada aquí. Hay tres formas de pegar texto dentro del área de execCommand
:
- Ctrl + V
- Haga clic derecho -> Pegar
- Haga clic derecho -> Pegar como texto sin formato
Quiero permitir pegar solo texto sin ningún tipo de marcado HTML. ¿Cómo puedo forzar las dos primeras acciones para pegar texto sin formato?
Solución posible: la forma en que se me ocurre es configurar oyente para eventos de teclado para (Ctrl + V) y quitar etiquetas HTML antes de pegar.
- ¿Es la mejor solución?
- ¿Es a prueba de balas para evitar que cualquier makup HTML pegado?
- ¿Cómo agregar oyente a Click derecho -> Pegar?
En IE11, execCommand no funciona bien. Utilizo el siguiente código para IE11 <div class="wmd-input" id="wmd-input-md" contenteditable=true>
es mi cuadro div.
Leo datos del portapapeles de window.clipboardData y modif textContent y doy cursivo.
Doy un tiempo de espera para configurar el cursor, porque si no establezco el tiempo de espera, un símbolo de intercalación va al final de div.
y deberías leer clipboardData en IE11 de la siguiente manera. Si no lo haces, el carácter de nueva línea no se maneja correctamente, por lo que el símbolo de intercalación falla.
var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;
Probado en IE11 y Chrome. Es posible que no funcione en 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);
Firefox no te permite acceder a los datos del portapapeles, por lo que tendrás que hacer un "truco" para que funcione. No he podido encontrar una solución completa, sin embargo, puedes arreglarla para ctrl + v pastas creando un área de texto y pegando a eso en su lugar:
//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);
}
Intercepte el evento de paste
, cancele el pegado e inserte manualmente la representación de texto del portapapeles: http://jsfiddle.net/HBEzc/ . Este debería ser el más confiable:
- Captura todo tipo de pegar (Ctrl + V, menú contextual, etc.)
- Te permite obtener los datos del portapapeles directamente como texto, para que no tengas que hacer feos hacks para reemplazar HTML.
Sin embargo, no estoy seguro del soporte para navegadores cruzados.
editor.addEventListener("paste", function(e) {
// cancel paste
e.preventDefault();
// get text representation of clipboard
var text = e.clipboardData.getData("text/plain");
// insert text manually
document.execCommand("insertHTML", false, text);
});
Ninguna de las respuestas publicadas parece funcionar de manera cruzada o la solución es demasiado complicada:
- El comando
insertText
no es compatible con IE - El uso del comando
paste
genera un error de desbordamiento de pila en IE11
Lo que funcionó para mí (IE11, Edge, Chrome y FF) es el siguiente:
$("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>
Tenga en cuenta que el controlador de pegado personalizado solo es necesario / funciona para los nodos contenteditable
. Sin embargo, tanto textarea
como input
campos de input
simple no admiten pegar contenido HTML en absoluto, por lo que no es necesario hacer nada aquí.
No pude obtener la respuesta aceptada aquí para trabajar en IE, así que hice un poco de exploración y llegué a esta respuesta que funciona en IE11 y las últimas versiones de Chrome y 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);
}
});
Por supuesto, la pregunta ya está respondida y el tema es muy antiguo, pero quiero brindarle mi solución, ya que es simple y está limpio:
Esto está dentro de mi evento de pegar en mi 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);
}
La parte else es de otra publicación SO que ya no pude encontrar ...
ACTUALIZACIÓN 19.11.2014: La otra publicación SO
También estaba trabajando en un texto sin formato y comencé a odiar todos los errores de execCommand y getData, así que decidí hacerlo de la manera clásica y funciona como un encanto:
$(''#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);
});
El código con mis anotaciones se puede encontrar aquí: http://www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript
Una solución cercana como pimvdb. Pero funciona de FF, Chrome e 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);
}
});
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();
}
}
El código anterior funciona para mí en IE10 e IE11 y ahora también funciona en Chrome y Safari. No probado en Firefox.