Commit 1a697a6d authored by Vincent Tam's avatar Vincent Tam

Post finished: JS Copy Btn

parent f69c0320
Pipeline #58693206 passed with stages
in 36 seconds
---
title: "JavaScript Copy Button"
date: 2019-04-26T23:01:53+02:00
categories:
- technical support
tags:
- JavaScript
- Math.SE
- HTML
draft: false
---
# Goal
To create a copy button for [my Math.SE comment template][1] in order to save
the trouble of copying and pasting.
# My first attempt
I put the boilerplate inside a Markdown codeblock to prevent them from getting
interpreted by [Hugo][8]'s Markdown parser. Under each codeblock, I placed the
copy button.
> ```
> Comment boilerplate goes here ...
> ```
>
> <button class="copyBtn">📝</button>
>
> ```
> Another comment boilerplate goes here ...
> ```
>
> <button class="copyBtn">📝</button>
>
> ...
[My page][1]'s original layout
{{< highlight js >}}
$(document).ready(function() {
$('.copyBtn').click(function() {
copy($(this).prev().children())
j });
});
function copy(selector) {
var screenTop = $(document).scrollTop();
var $temp = $("<div>");
$("body").append($temp);
$temp.attr("contenteditable", true)
.html($(selector).html()).select()
.on("focus", function() { document.execCommand('selectAll',false,null) })
.focus();
document.execCommand("copy");
$temp.remove();
$('html, body').scrollTop(screenTop);
}
{{< /highlight >}}
`static/js/copyBtn.js` at Git tag `copyBtn0`
It worked fine in [Qutebrowser][2], but _not_ in [Firefox][3]. Since the later
is a popular web browser, I would like to fix my script so that it would
function in [Firefox][3].
# My second attempt
I tried simplifying the code by getting rid of [jQuery][4], after realising that
the JavaScript substitute for the function `$(document).ready()` _wasn't_ simple
at all.
Some Google search pointed me to the concept of [event delegation][5]. An event
listener _universal for all copy buttons_ had to invoke a `doCopy(target)`
method, where `target` is the HTML element containing the comment to be copied.
I personally find that [JavaScript Info's page][6] is much easier to read.
Nevertheless, once the model has been understood, the code written in
[David Walsh's page][5] is much easier to be maintained.
{{< highlight js >}}
// https://davidwalsh.name/event-delegate
document.getElementsByTagName("article")[0].addEventListener("click", function(e) {
if (e.target && e.target.nodeName == 'BUTTON') { // only act on buttons
doCopy(e.target.previousElementSibling.firstElementChild);
}
});
// https://stackoverflow.com/a/33713926/3184351
function doCopy(target) {
var range = document.createRange();
range.selectNodeContents(target);
s = window.getSelection();
s.addRange(range);
document.execCommand('copy');
// Reset selection
// https://stackoverflow.com/a/3169849/3184351
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
s.empty();
} else if (window.getSelection().removeAllRanges) { // Firefox
s.removeAllRanges();
}
} else if (document.selection) { // IE?
document.selection.empty();
}
}
{{< /highlight >}}
This version of `static/js/copyBtn.js` _did_ work in both [Qutebrowser][2] and
[Firefox][3]. However, sometimes, the _whole_ page was copied instead of the
`target`. I had tried resetting the selection, but that _didn't_ work. My
primitive debugging technique `console.log(range)` _didn't_ work: the _whole_
page could get copied while the `range` behaved normally.
# Back to basics
Finally, I have decided to convert the codeblocks into immutable `<textarea>`s
because [W3Schools's tutorial][7] only works for _HTML form elements_. To
prevent [Hugo][8] from parsing the contents inside the `<textarea>`s, they have
to be wrapped with a `<div>` block. This allows a significant simplification of
the script.
{{< highlight js "linenos=table,hl_lines=8-9" >}}
document.getElementsByTagName("article")[0].addEventListener("click", function(e) {
if (e.target && e.target.nodeName == 'BUTTON') {
doCopy(e.target.previousElementSibling.firstElementChild);
}
});
function doCopy(target) {
target.select();
document.execCommand("copy");
}
{{< /highlight >}}
Refined version of `static/js/copyBtn.js` at commit [4a4f46d1][9].
[1]: /page/math-se-comment-templates/
[2]: //www.qutebrowser.org/
[3]: //www.mozilla.org/en-US/firefox/
[4]: //jquery.com/
[5]: //davidwalsh.name/event-delegate
[6]: //javascript.info/event-delegation
[7]: //www.w3schools.com/howto/howto_js_copy_clipboard.asp
[8]: //gohugo.io/
[9]: //gitlab.com/VincentTam/vincenttam.gitlab.io/commit/4a4f46d1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment