Building on Rox Dorentus's accepted answer (and with reference to jpc-ae's helpful Javascript conversion), here is an improvement on the algorithm that doesn't involve hacking the display
style of the <rt>
elements, which feels fragile to me.
Instead, we build an array of references to all the nodes in the selection, filter for any with the tag <rb>
, and return their innerText
. I also provide a commented-out alternative in case no <rb>
tags are used to wrap up the kanji.
document.addEventListener('copy', function (e) {
var nodesInRange = getRangeSelectedNodes(window.getSelection().getRangeAt(0));
/* Takes all <rb> within the selected range, ie. for <ruby><rb>振</rb><rt>ふ</rt></ruby> */
var payload = nodesInRange.filter((node) => node.nodeName === "RB").map((rb) => rb.innerText).join("");
/* Alternative for when <rb> isn't used: take all textNodes within <ruby> elements, ie. for <ruby>振<rt>ふ</rt></ruby> */
// var payload = nodesInRange.filter((node) => node.parentNode.nodeName === "RUBY").map((textNode) => textNode.textContent ).join("");
e.clipboardData.setData('text/plain', payload);
e.preventDefault();
/* Utility function for getting an array of references to all the nodes in the selection area,
* from: https://mcmap.net/q/689468/-js-get-array-of-all-selected-nodes-in-contenteditable-div */
function getRangeSelectedNodes(range) {
var node = range.startContainer;
var endNode = range.endContainer;
if (node == endNode) return [node];
var rangeNodes = [];
while (node && node != endNode) rangeNodes.push(node = nextNode(node));
node = range.startContainer;
while (node && node != range.commonAncestorContainer) {
rangeNodes.unshift(node);
node = node.parentNode;
}
return rangeNodes;
function nextNode(node) {
if (node.hasChildNodes()) return node.firstChild;
else {
while (node && !node.nextSibling) node = node.parentNode;
if (!node) return null;
return node.nextSibling;
}
}
}
});
user-select
: #827282 – Bingaman