Estoy trabajando en un proyecto en el que hemos decidido mostrarle al usuario, para facilitarle la tarea, un botón o enlace que le permita copiar un texto al portapapeles. De esa forma puede realizar dos acciones con un solo click: seleccionar y copiar.
Empecé la investigación (léase búsqueda en Google) y obtuve resultados demasiado buenos para ser verdad. Gracias al método “setData” del objeto “clipboardData” era sencillo cumplir mi objetivo:
clipboardData.setData("Text", s)
// siendo "s" una variable que contiene el texto a copiar
Pero no podía ser tan fácil. El código anterior sólo funciona en Internet Explorer, así que seguí buscando una solución cross-browser o, como mínimo, compatible con Firefox.
Después de algún intento infructuoso, encontré este ejemplo que funcionaba… con ciertas restricciones.
Por lo visto, las políticas de seguridad de Mozilla impiden la ejecución de dicho código. Y puede que con motivo, ya que de lo contrario cualquier página podría tener acceso a tu portapapeles y, por lo tanto, a información potencialmente delicada (direcciones de e-mail, teléfonos, números de tarjeta de crédito,… o cualquier otro dato que hayas copiado).
Llegados a este punto, existían dos opciones para poder copiar el texto en Firefox:
- Firmar el script, demasiado costoso para lo que queremos hacer
- Confiar en que el usuario tenga activada la opción “signed.applets.codebase_principal_support” en su configuración (about:config), o bien esté dispuesto a habilitarla
Si estamos pensando en hacer la aplicación más fácil de usar, no podemos pedirle al usuario que configure su navegador de una forma especial, que además conlleva cierto riesgo.
A todo esto hay que unir por un lado un código fuente bastante “farragoso”, lleno de capturas de excepciones y avisos al usuario. Y por otro, las alertas de seguridad que tanto IE como FF muestran antes de permitir la copia de contenidos al portapapeles. Seguro, pero poco práctico.
Me estaba replanteando la decisión de incluir esta funcionalidad cuando encontré una solución algo más limpia. Inicialmente había descartado la opción de implementarla usando Flash, pero al ver la alternativa de AddThis volví a cambiar de opinión.
La propuesta se basa en 4 elementos:
- La librería jQuery, versión 1.3.2 reducida, como apoyo.
- Un archivo de Flash, ZeroClipboard.swf, que fundamentalmente consiste en una imagen transparente que “captura” los eventos del ratón y copia un texto predefinido al portapapeles. Si teneis curiosidad por saber cómo está implementado, os recomiendo Sothink SWF Decompiler.
- Una clase, ZeroClipboard, que sirve de enlace entre el Flash y el Javascript de la página.
- El código Javascript que se ejecuta en la página y que analizaremos a continuación:
// Inicializamos el objeto ZeroClipboard con la ruta del fichero Flash
ZeroClipboard.setMoviePath('/lib/zeroclipboard/ZeroClipboard.swf');
// Usaremos como "disparadores" los elementos con class="copybtn"
$('.copybtn').each(function(){
// Para diferenciarlos deben llamarse copybtn[X], siendo [X] un entero
c = this.id.substr(7);
// Se crea un "cliente" para cada disparador
clips[c] = new ZeroClipboard.Client();
// El texto se tomará del elemento con id="txt[X]"
clips[c].setText($('#txt'+c).val());
// Al terminar de copiar, ejecutar la función "cplt" (más abajo)
clips[c].addEventListener('onComplete', cplt);
// Si no está oculto, "pegar" el Flash al elemento actual
// (es decir, colocarlo justo encima ocupando toda su extensión)
if (!$(this).hasClass('hidebtn')) clips[c].glue(this);
}).bind('toggle',function(e){
// Comportamiento cuando se cambie de un elemento a otro
...
});
// Al terminar, selecciona el texto copiado
// y muestra un mensaje de confirmación durante unos segundos
var cplt = function(client, text)
{
c = client.domElement.id.substr(7);
// Selecciona el contenido del "textarea"
// (si usamos otro elemento, habrá que modificar esta línea)
$('#txt'+c).get(0).select();
$('#copyfdbk'+c).html(' <em>Copied</em>').show();
setTimeout("$('#copyfdbk"+c+"').fadeOut()", 2500);
}
En cuanto al HTML, destacar sólo un par de detalles:
<!-- El contenedor del texto es un textarea con el id adecuado --> <textarea rows="5" id="txt0" wrap="off"... <!-- El disparador es un botón con el id y la clase indicados --> <input id="copybtn0" type="button" value="Copy Code" class="copybtn lgbtn" /> <!-- El elemento donde se mostrará el "feedback" --> <span id="copyfdbk0"></span>
Lo único que falta es ejecutar el código Javascript al terminar de cargar la página.
Más información sobre Zero Clipboard en Google Code.
Seleccionar el contenido de un span
Como comentaba más arriba, si queremos seleccionar el texto de un elemento que no sea un textarea tendremos que modificar la llamada:
// $('#txt'+c).get(0).select();
selectElement($('#text_to_copy_'+c).get(0))
// Siendo la función:
function selectElement (element)
{
if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
} else if (window.getSelection) {
var range = document.createRange();
range.selectNodeContents(element);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}
Os dejo un enlace a la fuente original con su correspondiente explicación.
¿Y si el usuario no tiene instalado el plugin de Flash?
Por último, si no queremos confiar el funcionamiento del script en la carga del Flash podemos hacer una pequeña modificación:
- Retomamos la función Javascript que copia un texto al portapapeles. Llamémosla “copyToClipboard”.
- En el bucle que recorre los disparadores, le asignamos a cada uno de ellos un gestor del evento “click” que llame a “copyToClipboard”.
- Hacemos un cambio en el método “glue” de la clase “ZeroClipboard.Client” para que coloque el Flash fuera de lugar:
// style.left = '' + box.left + 'px'; style.left = '0px'; // style.top = '' + box.top + 'px'; style.top = '0px';
- Y añadimos otro gestor de eventos al objeto cliente, para comprobar que se carga correctamente:
clips[c].addEventListener('onLoad', loaded);
// Colocar en su sitio si todo va bien
var loaded = function(client) {
c = client.domElement.id.substr(12);
clips[c].reposition();
}
style.top = '' + box.top + 'px';
7 comentarios ↓
Te pregunto algo… necesito realizar algo parecido… una serie de paginas en html con hipervinculos de botones en CSS, para generar un control de las pulsaciones, al pinchar el boton debo copiar en portapapeles el texto de ese hipervinculo -no la ruta- y pegarlo en un doc… tendras este ejemplo en algun zip para poder revisarlo y adaptarlo…
Gracias de Antemano…
Hola Josete,
No sé si te he entendido bien… ¿quieres enlazar un botón o un texto?
De todas formas, a partir del código que he incluido en el post es sencillo modificar el texto que se quiere copiar. Se trata de la siguiente línea:
clips[c].setText($('#txt'+c).val());Que podría quedar así:
clips[c].setText(this.innerHTML);Espero que te sirva. Si no, déjame ver el código fuente que quieres utilizar.
Un saludo
Hola Marcis… gracia por tu respuesta…. te explico…
tengo una serie de paginas dentro de una intranet… es todo muy basico en html y uso CSS para la apariencia de botones…
Mi necesidad se basa en: tengo botones en cada pagina los botones tienen este formato
TEXTO A COPIAR
Para realizar un seguimiento de la ruta que se sigue ya que es una estructura en arbol necesito que las personas que lo usen al pinchar en cada boton se copie en el portapapeles lo que aqui te refiero como TEXTO A COPIAR y ellos lo pegaran en unos archivos txt para luego hacer seguimiento…
Veras que es algo rudimentario, se que esto usando php u otro lenguaje podria hacer seguimiento de las paginas de una manera mas dinamica… pero en la actualidad es imposible…
Gracias por adelantado…
el formato del boton es “TEXTO A COPIAR“
he tratado de que no reconozca el html pero ha sido imposible…
seria abre parrafo abre hipervinculo TEXTO cierre hipervinculo cierre parrafo… lo que necesito copia es el TEXTO con cada pulsacion de un boton…
disculpa lo fastidioso…
Tranquilo Josete, he visto el código HTML en los correos de aviso de nuevos comentarios que recibo.
Siguiendo el ejemplo, tan solo habría que hacer un par de modificaciones:
Añadir una clase común a tus enlaces. Por ejemplo “copybtn”:
<a href="......." rel="nofollow">TEXTO A COPIAR</a>Y en el código original, hacer la modificación que te dije en el comentario anterior para seleccionar el texto del enlace:
$('.copybtn').each(function(){
...
clips[c].setText(this.innerHTML);
Espero que te sirva.
Un saludo
lo probare… muchas gracias… te aviso…
Escribe aquí tu comentario