Goal
To create a copy button for my Math.SE comment template 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’s Markdown parser. Under each codeblock, I placed the copy button.
Comment boilerplate goes here ...
Another comment boilerplate goes here ...
…
My page’s original layout
$(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);
}
static/js/copyBtn.js
at Git tag copyBtn0
It worked fine in Qutebrowser, but not in Firefox. Since the later is a popular web browser, I would like to fix my script so that it would function in Firefox.
My second attempt
I tried simplifying the code by getting rid of jQuery, 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. 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 is much easier to read.
Nevertheless, once the model has been understood, the code written in
David Walsh’s page is much easier to be maintained.
// 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();
}
}
This version of static/js/copyBtn.js
did work in both Qutebrowser and
Firefox. 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 only works for HTML form elements. To
prevent Hugo 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.
|
|
Refined version of static/js/copyBtn.js
at commit 4a4f46d1.