<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/atom.xsl" type="text/xsl"?><feed
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:thr="http://purl.org/syndication/thread/1.0"
	xml:lang="en-US"
	><title>Sacha Chua - category - css</title>
	<subtitle>Emacs, sketches, and life</subtitle>
	<link rel="self" type="application/atom+xml" href="https://sachachua.com/blog/category/css/feed/atom/index.xml" />
  <link rel="alternate" type="text/html" href="https://sachachua.com/blog/category/css" />
  <id>https://sachachua.com/blog/category/css/feed/atom/index.xml</id>
  <generator uri="https://11ty.dev">11ty</generator>
	<updated>2023-01-07T20:32:31Z</updated>
<entry>
		<title type="html">Using Javascript to add a "Copy code" link to source code blocks in my blog posts</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2023/01/using-javascript-to-add-a-copy-code-link-to-source-code-blocks-in-my-blog-posts/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2023-01-07T20:32:31Z</updated>
    <published>2023-01-07T20:32:31Z</published>
    <category term="css" />
<category term="js" />
<category term="blogging" />
		<id>https://sachachua.com/blog/2023/01/using-javascript-to-add-a-copy-code-link-to-source-code-blocks-in-my-blog-posts/</id>
		<content type="html"><![CDATA[<p>
I'd like to write about code more often. It's easier for people to try
out ideas if they can copy the code without fiddling with selecting
the text, especially on mobile browsers, so "Copy code" buttons on
source code blocks would be nice. I used <a href="https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html">this tutorial for adding code buttons</a> as a basis for the following CSS and JS code.
</p>

<p>
First, let's add the buttons with Javascript. I want the buttons to be
visible in the summary line if I'm using the <code>&lt;details /&gt;</code> element. If
not, they can go in the div with the <code>org-src-container</code> class.
</p>

<p>
</p><div class="org-src-container">
<pre class="src src-js"><span class="org-comment-delimiter">/* </span><span class="org-comment">Start of copy code</span><span class="org-comment-delimiter"> */</span>
<span class="org-comment-delimiter">// </span><span class="org-comment">based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html</span>
<span class="org-keyword">const</span> <span class="org-variable-name">copyLabel</span> = <span class="org-string">'Copy code'</span>;

<span class="org-keyword">async</span> <span class="org-keyword">function</span> copyCode(<span class="org-variable-name">block</span>, <span class="org-variable-name">button</span>) {
  <span class="org-keyword">let</span> <span class="org-variable-name">code</span> = block.querySelector(<span class="org-string">'pre.src'</span>);
  <span class="org-keyword">let</span> <span class="org-variable-name">text</span> = code.innerText;
  <span class="org-keyword">await</span> navigator.clipboard.writeText(text);
  button.innerText = <span class="org-string">'Copied'</span>;
  setTimeout(() =&gt; {
    button.innerText = copyLabel;
  }, 500);
}

<span class="org-keyword">function</span> <span class="org-function-name">addCopyCodeButtons</span>() {
  <span class="org-keyword">if</span> (!navigator.clipboard) <span class="org-keyword">return</span>;
  <span class="org-keyword">let</span> <span class="org-variable-name">blocks</span> = document.querySelectorAll(<span class="org-string">'.org-src-container'</span>);
  blocks.forEach((block) =&gt; {
    <span class="org-keyword">let</span> <span class="org-variable-name">button</span> = document.createElement(<span class="org-string">'button'</span>);
    button.innerText = copyLabel;
    button.classList.add(<span class="org-string">'copy-code'</span>);
    <span class="org-keyword">let</span> <span class="org-variable-name">details</span> = block.closest(<span class="org-string">'details'</span>);
    <span class="org-keyword">let</span> <span class="org-variable-name">summary</span> = details &amp;&amp; details.querySelector(<span class="org-string">'summary'</span>);
    <span class="org-keyword">if</span> (summary) {
      summary.appendChild(button);
    } <span class="org-keyword">else</span> {
      block.appendChild(button);
    }
    button.addEventListener(<span class="org-string">'click'</span>, <span class="org-keyword">async</span>() =&gt; {
      <span class="org-keyword">await</span> copyCode(block, button);
    });
    block.setAttribute(<span class="org-string">'tabindex'</span>, 0);
  });
}
document.addEventListener(<span class="org-string">"DOMContentLoaded"</span>, <span class="org-keyword">function</span>(<span class="org-variable-name">event</span>) { 
  addCopyCodeButtons();
});
<span class="org-comment-delimiter">/* </span><span class="org-comment">End of copy code</span><span class="org-comment-delimiter"> */</span>
</pre>
</div>

<p></p>

<p>
Then we style it:
</p>

<p>
</p><div class="org-src-container">
<pre class="src src-css"><span class="org-comment-delimiter">/* </span><span class="org-comment">Start of copy code</span><span class="org-comment-delimiter"> */</span>
<span class="org-css-selector">pre.src</span> { <span class="org-css-property">margin</span>: 0 }
<span class="org-css-selector">.org-src-container</span> {
    <span class="org-css-property">position</span>: relative;
    <span class="org-css-property">margin</span>: 0 0;
    <span class="org-css-property">padding</span>: 1.75rem 0 1.75rem 1rem;
}
<span class="org-css-selector">summary</span> { <span class="org-css-property">position</span>: relative; }
<span class="org-css-selector">summary .org-src-container</span> { <span class="org-css-property">padding</span>: 0 }
<span class="org-css-selector">summary .org-src-container pre.src</span> { <span class="org-css-property">margin</span>: 0 }
<span class="org-css-selector">.org-src-container button.copy-code, summary button.copy-code</span> {
    <span class="org-css-property">position</span>: absolute;
    <span class="org-css-property">top</span>: 0px;
    <span class="org-css-property">right</span>: 0px;
}
<span class="org-comment-delimiter">/* </span><span class="org-comment">End of copy code</span><span class="org-comment-delimiter"> */</span>
</pre>
</div>

<p></p>

<p>
Someday I'll figure out how to make it easier to tangle things to the
post's directory and make the file available for download. In the
meantime, this might be a good start.
</p>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2023%2F01%2Fusing-javascript-to-add-a-copy-code-link-to-source-code-blocks-in-my-blog-posts%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry>
</feed>