skip to Main Content

I’m trying to write a chrome extension that makes a simple pop-up when you (for example) ctrl+click a character (it’s a simple tool for helping to remember keywords for Japanese Kanji).

This is what I’ve got for an attempt so far – the alert() is just a placeholder to quickly show me what I’ve seen:

document.addEventListener("click", (event) => {
    if (event.ctrlKey) {
        let selection = document.getSelection();
        if (selection.rangeCount == 1) {
            let letter = selection.focusNode.data[selection.focusOffset];

            alert(letter);
        }
    }
});

This sometimes gets the right character – e.g. on this article, at the start of the article (the orange characters, 石川県...), it’ll get the first two ( and ) correct, but display undefined for the third (i.e. should be ). Here’s a snippet:

document.addEventListener("click", (event) => {
    if (event.ctrlKey) {
        let selection = document.getSelection();
        if (selection.rangeCount == 1) {
            let letter = selection.focusNode.data[selection.focusOffset];
            console.log(letter);
        }
    }
});
<p><span class="colorL"><ruby>石川県<rt>いしかわけん</rt></ruby><ruby>七尾市<rt>ななおし</rt></ruby></span><span class="colorB">で</span><span class="colorB">、</span><span class="color4"><ruby>先月<rt>せんげつ</rt></ruby></span><span class="colorB">の</span><span class="color3"><ruby>地震<rt>じしん</rt></ruby></span><span class="colorB">の</span><span class="color4">あと</span><span class="color1"><ruby><span class="under">避難</span><rt>ひなん</rt></ruby></span><span class="colorB">を</span><span class="color3"><ruby>続<rt>つづ</rt></ruby>け</span><span class="colorB">て</span><span class="color4">い</span><span class="colorB">る</span><span class="color4"><ruby>人<rt>ひと</rt></ruby></span><span class="colorB">の</span><span class="color3">ため</span><span class="colorB">に</span><span class="colorB">、</span><span class="colorC"><ruby>韓国大使館<rt>かんこくたいしかん</rt></ruby></span><span class="colorB">の</span><span class="color4"><ruby>人<rt>ひと</rt></ruby></span><span class="colorB">や</span><span class="colorL"><ruby>日本<rt>にっぽん</rt></ruby></span><span class="colorB">に</span><span class="color4"><ruby>住<rt>す</rt></ruby>ん</span><span class="colorB">で</span><span class="color4">いる</span><span class="color4"><ruby>韓国人<rt>かんこくじん</rt></ruby></span><span class="colorB">などが</span><span class="color4"><ruby>料理<rt>りょうり</rt></ruby></span><span class="colorB">を</span><span class="color4"><ruby>作<rt>つく</rt></ruby>り</span><span class="colorB">ました</span><span class="colorB">。</span></p>

If I log the values, I can see that the index I’m using (selection.focusOffset) is simply out of bounds for the data (selection.focusNode.data) that it’s got. I can see there are three other *offset/*Node.data pairs, but they seem to all contain the same information so I’ve no idea which one would be the "right" one or when they would be different.

How can I reliably get the character that’s been clicked on?

2

Answers


  1. Chosen as BEST ANSWER

    I went with a suggestion from @Gaberocksall, which involved changing the input method. Now I get all the selected text and, if it's ctrl+clicked-on, see the alert:

    document.addEventListener("click", (event) => {
        if (event.ctrlKey) {
            let selection = document.getSelection().toString();
    
            if (selection.length == 1) {
                alert(selection);
            }
        }
    });
    
    

    (N.B. I found you could highlight hyperlink text by holding the Alt key, which is useful here too, and this solution will do for me as it's a hacky self-project only at this stage)


  2. selection.focusOffset might be pointing to the wrong index within the string. One potential solution is to get the character directly from the clicked element’s text content rather than relying on the selection API.

    document.addEventListener("click", (event) => {
        if (event.ctrlKey) {
            let targetElement = event.target;
            if (targetElement.nodeType === Node.TEXT_NODE) {
                let letter = targetElement.textContent.trim();
                if (letter.length === 1) {
                    alert(letter);
                }
            }
        }
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search