JavaScript

Cómo agregar boton Copiar al portapapeles a bloques de código

January 8, 2023

Cómo agregar boton Copiar al portapapeles a bloques de código

Cuando comencé a escribir mi sitio web y adjunté algunos bloques de código al artículo para completar lo que escribí. Pensé, que sería bueno agregar botones de copia al portapapeles . Si un visitante desea copiar un ejemplo de código o un comando de shell, es bueno poder simplemente presionar un botón en lugar de seleccionar manualmente el texto, hacer clic con el botón derecho y presionar copiar.

Una búsqueda rápida me llevó a una publicación de Rob O'Leary . Usé el ejemplo de Rob, pero hice algunos cambios. En mi sitio, configuré la biblioteca highlight.js porque es un programa de resaltado de código simple. Así es como agregué esta característica a mi sitio web.

Usando Highlight.js en tu página web

Para incorporar highlight.js en su página web, visite highlightjs.org y copie sus enlaces jsdelivr CSS y JavaScript listos para usar en el elemento principal de su archivo HTML:

<head>
    <!-- highlightjs  -->
    <link rel="stylesheet"
     href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/styles/stackoverflow-dark.min.css">
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/highlight.min.js"></script>
</head>

Tenga en cuenta que el enlace de estilos, que descargó de highlightjs.org. Es diferente del ejemplo. Puede cambiar el tema del código de muestra con cualquier otro estilo. En mi sitio web estoy usando los estilos de stackoverflow-dark.min.css

Una vez que haya agregado los enlaces a la hoja de estilo CSS y los archivos JavaScript, solo hay una línea más de código que debe escribir en el elemento principal de su página HTML. Agregue este script para activar highlight.js y configúrelo para que funcione en su código de ejemplo:

<head>
    <!-- highlightjs  -->
    <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/styles/stackoverflow-dark.min.css">
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/highlight.min.js"></script>
    <script>hljs.highlightAll();</script>
</head>

Agregar código de ejemplo a su página web

Una vez que su página web esté equipada para usar highlight.js, puede agregar el código de ejemplo que desea mostrar a sus lectores. Simplemente escriba el código de ejemplo en la ubicación elegida dentro del elemento del cuerpo de su página HTML y enciérrelo dentro de las etiquetas de apertura <pre> <code> y cierre </code> </pre> . En caso de que tenga curiosidad, la etiqueta pre se refiere al texto con formato previo y la etiqueta de código indica una sección de código.

Una ventaja de usar highlight.js es que puede adaptar su enfoque de resaltado al lenguaje de programación que desea mostrar. Para indicarle a highlight.js qué lenguaje de programación está usando, agregue el texto 'language-' seguido del nombre del idioma como una clase dentro de la etiqueta de apertura <code>. Por ejemplo, podría mostrar código HTML en su página web de esta manera:

<!DOCTYPE html>
 <html lang="es-ES">
   <head>
      <title>highlight</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link rel="stylesheet" type="text/css" href="main.css">
       <!-- highlightjs  -->
      <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/styles/stackoverflow-dark.min.css">
      <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/highlight.min.js"></script>
      <script>hljs.highlightAll();</script>
  </head>
   <body>
     <div class="highlight">
      <pre>
       <code class="language-html">

        ...

      </code>
     </pre>
    </div>
  </body>
</html>

Crear botón

He visto muchas implementaciones diferentes de botones de código de copia, el botón se encuentra en la esquina superior derecha o inferior derecha del bloque de código. Sin embargo, he notado que el botón puede cubrir parte del código si la línea es demasiado larga, especialmente en dispositivos móviles.

Para evitar esta posibilidad, coloqué el botón en la esquina superior derecha del <header>. En nuestro código Java Script, agregaremos un elemento <header> a la clase "highlight.div" existente, donde se colocará nuestro botón junto con el nombre del idioma.

Solo queremos señalar los elementos anteriores que contienen el elemento de código.  En esto sitio, estoy usando ckeditor con función highlight, que agrega automáticamente una clase de idioma 'language-' dentro de la etiqueta de apertura <code> . Ahora, en nuestro código Java Script, agregaremos un elemento <header> a la clase "highlight.div"

Código Java Script que agregará nuestro botón:

(function () { 
			
		const copyButtonLabel = "Copia código"; 
		
		function addHeaderToHighlight() {
			const blocks = document.querySelectorAll('div.highlight');
                  blocks.forEach((block) => { 
			      const langName = getLanguage(block);
			      const header = document.createElement('header');
			      const span = document.createElement('span');
				  span.className = 'copy-code-name';
			      const button = document.createElement('button');
			      button.className = 'copy-code-button';
		          button.type = 'button';

			      span.innerText = langName;

			      if(langName.length === 0) {
				        span.classList.add('no-lang');
			         }

			    button.innerText = copyButtonLabel;
               
				block.insertBefore(header, block.childNodes[0])
				header.appendChild(span);

				button.addEventListener("click", copyCode);
				header.appendChild(button);
	    	});

		}
        addHeaderToHighlight(); 
 });

Ya tenemos la función addHeaderToHighlight, que creará el elemento <header>, donde se colocará el botón y el nombre del idioma, pero no funcionará. Como te habrás dado cuenta en el código hay una nueva función, "getLanguage", que no hemos escrito, y es la encargada de mostrar el nombre del idioma.

Así que agreguemos nuevas funciones a nuestro código Java Script:

	function getLanguage(block) {
		let lang = "";
		const code = block.querySelector('code');
		const dataLangAttribute = code.getAttribute('class');
		if(dataLangAttribute !== null && dataLangAttribute.length > 0) {
			const langArray = dataLangAttribute;
            const partsArray = langArray.split('-',2);
			const langText = partsArray[0];
			const nameCode = partsArray[1];
			lang = formatLanguage(nameCode);

		  }

        return lang;          
	}

En nuestra función "getLanguage" que nos dará el nombre del idioma, agregamos una nueva función "formatLanguage" que usa el método toUpperCase() para convertir el nombre del idioma obtenido de la función "getLanguage" a un nombre en mayúsculas.

Así que agreguemos la función "formatLanguage" a nuestro código Java Script:

	function formatLanguage(name) {
		 let formattedLang = '';
		 let lowercaseName = name.toLowerCase();
		 let acronymNames = ['html','css','php','json','jsonc','xml'];

		 if(acronymNames.includes(lowercaseName)) {
			formattedLang = name.toUpperCase();
		 } else if( lowercaseName === 'plaintext') {
			formattedLang = '';
		 } else if(name === 'javascript') {
			formattedLang = 'JavaScript';
		 } else {
			const capitalisedName = name.replace(/^\w/,(n) => n.toUpperCase());
			formattedLang = capitalisedName;
		 }
		
		return formattedLang;
	}

Diseño de botón

Los botones se han convertido en una parte esencial del desarrollo de los frentes. Por lo tanto, es importante tener en cuenta varias cosas antes de diseñar los botones.

Para colocar el botón en la esquina superior derecha, configuramos "highlight.div" en posición: relativa y el botón en posición: absoluta, y configuramos las propiedades superior y derecha de nuestro botón.

Para diseñar el botón, usé este CSS:


.highlight {
   margin: 0;
   padding:10px;
   background: #1c1b1b;
   border-radius: 6px;
   box-shadow: 0 .125rem .25rem rgba(0,0,0,.075)!important;
   position: relative;
}

.highlight pre {
   border-radius: 6px;
   padding-top: 10px;
}

.highlight code {
  -ms-overflow-style: none; 
  scrollbar-width: none;
  overflow-y: scroll; 
}
.highlight code::-webkit-scrollbar {
	display: none; 
  }

 pre[class*="language-"] {
  margin: 5px;
}

.copy-code-name {
   color: #F1F2F3 !important;
   letter-spacing: 0.10em;
   padding: 6px 12px; 
   font-weight: 600;
}
.copy-code-button {
	border-radius: 0;
	min-width: 55px;
	background: none repeat scroll 0 0 transparent;
	background-color: rgba(34, 136, 150, 1);
	color: #F1F2F3 !important;
	cursor: pointer;
	border-style: none;
	font-size: 0.8em;
	text-align: center;
	text-decoration: none;
	text-indent: 0;
	text-transform: uppercase;
	font-weight: 400;
	line-height: 1.50rem;
	word-spacing: 0.03em;
	margin: 0;
	position: absolute;
	display: block;
	margin-left: auto;
	margin-right: 0;
	padding: 6px 12px; 
	border-bottom-left-radius: 5px;
	border-top-right-radius: 6px;
	right: 0;
	top:0;
}

.copy-code-button > span {
	color: #F1F2F3 !important;
}

.copy-code-button, ::before, ::after {
	box-sizing: inherit;
}

.copy-code-button::before {
	content: '';
	display: inline-block;
	width: 16px;
	height: 16px;
	margin-right: 5px;
	background-size: contain;
	background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTUgMTUiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUwLjIgKDU1MDQ3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5QYWdlIDE8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iRmxvdyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IkJ0dG5faHRtbCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTgxOS4wMDAwMDAsIC03NTMuMDAwMDAwKSIgZmlsbD0iI0ZGRkZGRiI+CiAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMTEuMDAwMDAwLCA0MDUuMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iR3JvdXAtMiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTA4LjAwMDAwMCwgMzQyLjAwMDAwMCkiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMy45NzcyNzI3LDYgTDMuNDA5MDkwOTEsNiBDMi44NDQ1NDU0NSw2IDIuMzg2MzYzNjQsNi40NTgxODE4MiAyLjM4NjM2MzY0LDcuMDIyNzI3MjcgTDIuMzg2MzYzNjQsMTcuNTkwOTA5MSBDMi4zODYzNjM2NCwxOC4xNTU0NTQ1IDIuODQ0NTQ1NDUsMTguNjEzNjM2NCAzLjQwOTA5MDkxLDE4LjYxMzYzNjQgTDEzLjk3NzI3MjcsMTguNjEzNjM2NCBDMTQuNTQxODE4MiwxOC42MTM2MzY0IDE1LDE4LjE1NTQ1NDUgMTUsMTcuNTkwOTA5MSBMMTUsNy4wMjI3MjcyNyBDMTUsNi40NTgxODE4MiAxNC41NDE4MTgyLDYgMTMuOTc3MjcyNyw2IFogTTE0LjMxODE4MTgsMTcuNTkwOTA5MSBDMTQuMzE4MTgxOCwxNy43NzkwOTA5IDE0LjE2NTQ1NDUsMTcuOTMxODE4MiAxMy45NzcyNzI3LDE3LjkzMTgxODIgTDMuNDA5MDkwOTEsMTcuOTMxODE4MiBDMy4yMjA5MDkwOSwxNy45MzE4MTgyIDMuMDY4MTgxODIsMTcuNzc5MDkwOSAzLjA2ODE4MTgyLDE3LjU5MDkwOTEgTDMuMDY4MTgxODIsNy4wMjI3MjcyNyBDMy4wNjgxODE4Miw2LjgzNDU0NTQ1IDMuMjIwOTA5MDksNi42ODE4MTgxOCAzLjQwOTA5MDkxLDYuNjgxODE4MTggTDEzLjk3NzI3MjcsNi42ODE4MTgxOCBDMTQuMTY1NDU0NSw2LjY4MTgxODE4IDE0LjMxODE4MTgsNi44MzQ1NDU0NSAxNC4zMTgxODE4LDcuMDIyNzI3MjcgTDE0LjMxODE4MTgsMTcuNTkwOTA5MSBaIE0xMS45MzE4MTgyLDE5Ljk3NzI3MjcgQzExLjkzMTgxODIsMjAuMTY1NDU0NSAxMS43NzkwOTA5LDIwLjMxODE4MTggMTEuNTkwOTA5MSwyMC4zMTgxODE4IEwxLjAyMjcyNzI3LDIwLjMxODE4MTggQzAuODM0NTQ1NDU1LDIwLjMxODE4MTggMC42ODE4MTgxODIsMjAuMTY1NDU0NSAwLjY4MTgxODE4MiwxOS45NzcyNzI3IEwwLjY4MTgxODE4Miw5LjQwOTA5MDkxIEMwLjY4MTgxODE4Miw5LjIyMDkwOTA5IDAuODM0NTQ1NDU1LDkuMDY4MTgxODIgMS4wMjI3MjcyNyw5LjA2ODE4MTgyIEwxLjM2MzYzNjM2LDkuMDY4MTgxODIgTDEuMzYzNjM2MzYsOC4zODYzNjM2NCBMMS4wMjI3MjcyNyw4LjM4NjM2MzY0IEMwLjQ1ODE4MTgxOCw4LjM4NjM2MzY0IDAsOC44NDQ1NDU0NSAwLDkuNDA5MDkwOTEgTDAsMTkuOTc3MjcyNyBDMCwyMC41NDE4MTgyIDAuNDU4MTgxODE4LDIxIDEuMDIyNzI3MjcsMjEgTDExLjU5MDkwOTEsMjEgQzEyLjE1NTQ1NDUsMjEgMTIuNjEzNjM2NCwyMC41NDE4MTgyIDEyLjYxMzYzNjQsMTkuOTc3MjcyNyBMMTIuNjEzNjM2NCwxOS42MzYzNjM2IEwxMS45MzE4MTgyLDE5LjYzNjM2MzYgTDExLjkzMTgxODIsMTkuOTc3MjcyNyBaIiBpZD0iUGFnZS0xIj48L3BhdGg+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==");
	background-repeat: no-repeat;
}

  
 .copy-code-button:focus
  {
	background-color: rgba(2, 42, 63, 1) ;
	color: #ffffff !important;
	font-weight: 400;
	outline: 0;
} 
.copy-code-button:hover,
.copy-code-button:active {
	background-color: rgba(34, 136, 150, 1);
	color: #F1F2F3 !important;
	font-weight: 600;

}

Puede colocar el botón en otro lugar y darle un estilo diferente si lo prefiere.

¿Cómo obtengo el texto del elemento de code?

Guardar en el portapapeles del sistema es bastante simple. Hay una API del navegador, la API del portapapeles, que le permite leer y escribir en el portapapeles del sistema de forma asíncrona. El soporte del navegador es excelente (para escribir en el portapapeles). Se recomienda utilizar la API del portapapeles en lugar del método obsoleto document.execCommand().

Para acceder al portapapeles, use el navigator.clipboard global. La función asincrónica writeText () se utiliza para escribir en el portapapeles.

	
	 await navigator.clipboard.writeText("algún texto");

En el controlador de eventos de clic de botón, necesitamos obtener el elemento indirectamente. Necesitamos obtener el elemento principal del botón y luego ejecutar querySelector ("code") en él para obtener una referencia al elemento de código secundario. Luego, podemos obtener texto a través de la propiedad innerText del elemento.

Pongamos todo junto entonces.

    (function() { 
			
	const copyButtonLabel = "Copia código"; 

		    
	init();
	function init() {
	addHeaderToHighlight();
	}


		function addHeaderToHighlight() {
			const blocks = document.querySelectorAll('div.highlight');
                  blocks.forEach((block) => { 
			            const langName = getLanguage(block);
			            const header = document.createElement('header');
			            const span = document.createElement('span');
						span.className = 'copy-code-name';
			            const button = document.createElement('button');
			            button.className = 'copy-code-button';
		                button.type = 'button';

			           span.innerText = langName;

			          if(langName.length === 0) {
				              span.classList.add('no-lang');
			            }

			    button.innerText = copyButtonLabel;
               
				block.insertBefore(header, block.childNodes[0])
				header.appendChild(span);

				button.addEventListener("click", copyCode);
				header.appendChild(button);
	    	});

		}
		
	function getLanguage(block) {
		let lang = "";
		const code = block.querySelector('code');
		const dataLangAttribute = code.getAttribute('class');
		if(dataLangAttribute !== null && dataLangAttribute.length > 0) {
			const langArray = dataLangAttribute;
            const partsArray = langArray.split('-',2);
			const langText = partsArray[0];
			const nameCode = partsArray[1];
			lang = formatLanguage(nameCode);

		  }

        return lang;          
	}

	function formatLanguage(name) {
		 let formattedLang = '';
		 let lowercaseName = name.toLowerCase();
		 let acronymNames = ['html','css','php','json','jsonc','xml'];

		 if(acronymNames.includes(lowercaseName)) {
			formattedLang = name.toUpperCase();
		 } else if( lowercaseName === 'plaintext') {
			formattedLang = '';
		 } else if(name === 'javascript') {
			formattedLang = 'JavaScript';
		 } else {
			const capitalisedName = name.replace(/^\w/,(n) => n.toUpperCase());
			formattedLang = capitalisedName;
		 }
		
		return formattedLang;
	}

    async function copyCode(event) {
        const button = event.srcElement;
        const parent = button.parentElement.parentElement;
        let code = parent.querySelector("code");
        let text = code.innerText;

        await navigator.clipboard.writeText(text);
		      button.innerText = "Código copiado";
        setTimeout(()=> {
              button.innerText = copyButtonLabel;
        },
		1000);
	}
})();

Tenga en cuenta que este script debe cargarse después del contenido de la página, preferiblemente al final del cuerpo para que no bloquee la visualización. De lo contrario, el selector podría ejecutarse antes de que existan realmente los bloques de código.

Si bien el código anterior funciona perfectamente, no informamos al usuario que se copió el código. Por lo tanto, sería bueno hacer algo para indicarle al usuario que la tarea se completó con éxito.

¿Cómo informar al usuario que el texto del elemento de code se copió con éxito?

La mejor solución es cambiar el texto del botón a "Código copiado" cuando se realiza la acción, y restablecerlo a "Copia código" después de un segundo a través de setTimeout().

     
 button.innerText = "Código copiado";
     
   setTimeout(()=> {
     
           button.innerText = copyButtonLabel;
      
    },1000)

Si no le gusta el hecho de que el botón aumenta de tamaño cuando se cambia el texto, puede establecer un ancho mínimo igual al ancho del botón con el texto más largo.

Puede proporcionar la información al usuario de otra manera, por ejemplo, puede mostrar una animación de algún tipo. Pero para mí, esta fue la forma más precisa de decirle a un usuario que el contenido se copió con éxito en el portapapeles.

Búsqueda

Categorías

Últimos artículos

Cómo implementar el soporte de sitios web multilingües usando PHP

Cómo implementar el soporte de sitios web multilingües usando PHP

April 16, 2023

Lee mas
Lista de países con bandera y código de 2 dígitos y prefijo internacional de país

Lista de países con bandera y código de 2 dígitos y prefijo internacional de país

April 17, 2023

Lee mas
Validación de datos del formulario usando PHP y AJAX

Validación de datos del formulario usando PHP y AJAX

January 8, 2023

Lee mas
Integración de Google reCAPTCHA con el formulario de contacto

Integración de Google reCAPTCHA con el formulario de contacto

January 8, 2023

Lee mas
Este sitio web está utilizando cookies para analizar nuestros servicios. Puedes aceptar todas las cookies pulsando el botón "Aceptar". Para más información, consultaPolítica de Cookies
Aceptar