Resaltador de código con boton para copiar

highlighter with copy button pure js without jquery

Resaltador de código optimizado usando highlight.js que incluye el botón para copiar

highlight resaltador de codigo con boton de copiar sin jquery

Si tienes un blog que trata sobre desarrollo, es probable que hayas tenido que mostrar código a tus usuarios. Es por eso que hoy te muestro como agregar un resaltador de código que cuenta con un botón para copiar, que además he optimizado para solo cargar el script cuando sea necesario.

Instalar

Primero vas a necesitar incluir los siguientes estilos en tu proyecto. Por ejemplo, si usas WordPress puedes incluirlo en el archivo style.css. En Blogger debes usar la opción Agregar CSS de la plantilla. Para todos los demás, puedes incluirlo en el <head> de tu sitio.

pre {
  --copy-text: "Copy";
  --copied-text: "Copied!";
  --code-bg: #2a2833;
  --code-color: #9a92be;
  --code-color-comment: #787878;
  --code-color-string: #ffcd81;
  --code-color-selector: #a38eff;
  --code-color-property: #a38eff;
  --code-color-tag: #6a6684;
  --code-color-tag-name: #eeebfe;
  --code-color-attr: #c3b6ff;
  --code-color-class: #eeebff;
  --code-color-number: #ffcd95;
  position: relative;
  font-family: SFMono-Regular, monospace;
}

pre > code {
  background-color: var(--code-bg);
  color: var(--code-color);
  display: block;
  overflow-x: auto;
  white-space: pre;
  padding: 1.5rem;
  border-radius: 0.5rem;
}

.hljs-copy {
  position: absolute;
  border: 0 none;
  inset: 0.5rem 0.5rem auto auto;
  padding: 0.5rem 0.75rem;
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  cursor: pointer;
  border-radius: 0.5rem;
  transition: 0.3s background-color;
  font-size: 14px;
}

.hljs-copy:hover { background-color: rgba(0, 0, 0, 0.75) }
.hljs-copy::before { content: var(--copy-text) }
.hljs-copy.is-copied::before { content: var(--copied-text) }

.hljs-name,
.hljs-section { color: var(--code-color-tag-name) }
.hljs-tag { color: var(--code-color-tag) }
.hljs-selector-class { color: var(--code-color-class) }
.hljs-number { color: var(--code-color-number) }

.hljs-comment,
.hljs-meta { color: var(--code-color-comment) }

.hljs-emphasis,
.hljs-quote,
.hljs-string,
.hljs-strong,
.hljs-template-variable,
.hljs-variable { color: var(--code-color-string) }

.hljs-keyword,
.hljs-selector-tag,
.hljs-type { color: var(--code-color-selector) }

.hljs-attribute,
.hljs-bullet,
.hljs-literal,
.hljs-symbol { color: var(--code-color-property) }

.hljs-attr,
.hljs-selector-attr,
.hljs-selector-id,
.hljs-selector-pseudo,
.hljs-title { color: var(--code-color-attr) }

Ahora el script, el cual se encargará de agregar el botón de copiar y de cargar el resaltador de código. Debes incluirlo arriba de tu etiqueta </body> o si posees un archivo main.js puedes incluirlo ahí.

<script>/*<![CDATA[*/
async function initHighlight() {
  const loadScript = () => new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js'
    script.onload = resolve
    script.onerror = reject
    document.body.appendChild(script)
  })

  try {
    await loadScript()
    hljs.highlightAll()
  } catch (error) {
    console.error(error)
  }
}

function addCopyButtons (codeBlock) {
  if (!navigator || !navigator.clipboard) return

  const clipboard = navigator.clipboard
  const $button = document.createElement('button')
  $button.className = 'hljs-copy'

  $button.addEventListener('click', function () {
    clipboard.writeText(codeBlock.textContent).then(function () {
      $button.blur()
      $button.classList.add('is-copied')
      $button.disabled = true

      setTimeout(function () {
        $button.disabled = false
        $button.classList.remove('is-copied')
      }, 2000)
    })
  })

  const pre = codeBlock.parentNode
  pre.insertBefore($button, pre.firstChild)
}

function initCodeBlocks () {
  const codeBlocks = document.querySelectorAll('pre > code')
  if (codeBlocks.length === 0) return

  initHighlight()
  
  codeBlocks.forEach(function (codeBlock) {
    addCopyButtons(codeBlock)
  })
}

initCodeBlocks()/*]]>*/
</script>

Lo que hace este script es agregar un botón que al hacer clic copia el contenido del código. Además, carga el resaltador de código, pero solo realizará ambas acciones cuando exista al menos un bloque de código en la página.

Modo de uso

Para usarlo solo debes agregar un bloque de código en tu página. Entendiendo que el código debe estar dentro de una etiqueta <pre> y <code> como se muestra a continuación:

<pre><code>
  // Tu código aquí
</code></pre>

Por lo general highlightJs detectará el lenguaje que haz utilizado, pero para los casos que esto no sea suficiente o falle, puedes especificar el lenguaje usando una clase:

<pre><code class="language-html">
  // Tu código aquí
</code></pre>

Además recuerda que es muy importante que todo el código HTML se encuentre escapado. Para ayudarte con esta tarea he desarrollado una herramienta para escapar código html que puedes usar.

Personalizar

Si deseas personalizar el resaltador de código, puedes hacerlo modificando las variables que se encuentran al inicio del código CSS. Estas variables son las que se encargan de cambiar los colores del resaltador de código y otros estilos importantes.

He tratado de ser muy descriptivo con los nombres de las variables, pero si tienes alguna duda puedes preguntarme en los comentarios. Por si te lo preguntas, los estilos están inspirados del tema Duotone Dark del desarrollador Simurai.

Observaciones

Ten en cuenta que en todo el articulo se asume que usas los bloques <pre> y <code> solo para mostrar código. Si usas estas etiquetas para mostrar otro tipo de contenido, es probable que necesites realizar algunos cambios.

Conclusión

Realmente deseo que este articulo te haya ayudado de alguna forma si llegaste aquí buscando una solución para agregar un resaltador de código con un botón para copiar. Si tienes alguna duda o sugerencia puedes dejarla en los comentarios.