1

I would like to return the selected text when pressing a shortcut.

Here's my code :

chrome.commands.onCommand.addListener(function(command) {
    console.log(window.getSelection().toString());
});

It returns nothing, even if I have currenctly selected text. If I remove toString, here's the output :

anchorNode: null
anchorOffset: 0
baseNode: null
baseOffset: 0
extentNode: null
extentOffset: 0
focusNode: null
focusOffset: 0
isCollapsed: true
rangeCount: 0
type: "None"

Any idea about how I could actually return my selection?

Rob W
  • 341,306
  • 83
  • 791
  • 678
Gaston Flanchard
  • 519
  • 1
  • 5
  • 15
  • 1
    It may be that it's within an `iframe`, check if it's selecting the correct *window*. – MackieeE Dec 16 '13 at 09:24
  • You have a missing parenthesis in your code. – Manishearth Dec 16 '13 at 09:25
  • MackieeE : thank you. How could I do this? – Gaston Flanchard Dec 16 '13 at 09:26
  • 1
    Done some extension API checking, but this answer might help you alot! http://stackoverflow.com/a/3072535/292735 – MackieeE Dec 16 '13 at 10:01
  • 2
    and this! http://stackoverflow.com/questions/2626859/chrome-extension-how-to-capture-selected-text-and-send-to-a-web-service – MackieeE Dec 16 '13 at 10:07
  • @MackieeE: I really wonder how this answer might be of any use to the OP: http://stackoverflow.com/questions/3030738/getting-current-window-on-a-popup-google-chrome-extension/3072535#3072535 – gkalpak Dec 16 '13 at 10:27
  • @ExpertSystem After seeing the second answer I linked, I realised that was the case too ;) Thus apologies, would you like me to remove it? – MackieeE Dec 16 '13 at 10:29
  • I don't want you to remove it (unless you think it will confuse the OP). I am not an administrator of mod - I only decide about my own comments :) – gkalpak Dec 16 '13 at 10:43

1 Answers1

7

The listener is added in your background page, so window.getSelection() refers to the text selected in your (automatically generated) background page, not in the active tab. In order to retrieve the selected text from the active tab, you need to inject a little code to do it for you and report back with the result.

E.g.:

background.js:

/* The function that finds and returns the selected text */
var funcToInject = function() {
    var selection = window.getSelection();
    return (selection.rangeCount > 0) ? selection.toString() : '';
};

/* This line converts the above function to string
 * (and makes sure it will be called instantly) */
var jsCodeStr = ';(' + funcToInject + ')();';

chrome.commands.onCommand.addListener(function(cmd) {
    if (cmd === 'selectedText') {
        /* Inject the code into all frames of the active tab */
        chrome.tabs.executeScript({
            code: jsCodeStr,
            allFrames: true   //  <-- inject into all frames, as the selection 
                              //      might be in an iframe, not the main page
        }, function(selectedTextPerFrame) {
            if (chrome.runtime.lastError) {
                /* Report any error */
                alert('ERROR:\n' + chrome.runtime.lastError.message);
            } else if ((selectedTextPerFrame.length > 0)
                    && (typeof(selectedTextPerFrame[0]) === 'string')) {
                /* The results are as expected */
                alert('Selected text: ' + selectedTextPerFrame[0]);
            }
        });
    }
});

manifest.json:

{
    "manifest_version": 2,

    "name":    "Test Extension",
    "version": "0.0",

    "background": {
        "persistent": false,
        "scripts": ["background.js"]
    },

    "permissions": ["<all_urls>"],

    "commands": {
        "selectedText": {
            "description": "Retrieve the selected text in the active tab"
        }
    }
}

One more thing to note:

Accroding to this answer (and my own experience with Chrome v31) the official docs on declaring a keyboard shortcut (a.k.a. command) are falsely stating you can set the key-combination programmatically.
The truth (as "stolen" from the aforementioned answer) is:

On Chrome 29 (and later) you have to navigate to chrome://extensions/ and scroll down to the bottom of the page. On the right side there is a button Keyboard shortcuts.

Modal dialog pops up with all extensions that have registered some commands in their manifest file. But the shortcuts itself are Not set so the user must set them manually.

(emphasis mine)

UPDATE:

The whole truth is this:

  • If the suggested_key is not already in use as a keyboard shortcut on the user's platform, then the binding works as expected.

  • If the suggested_key is already bound to a different command, then the binding is not set up. The user has to navigate to chrome://extensions/ and click the Keyboard shortcuts button at the bottom of the page. In the dialog that pops up, the user has to assign a shortcut to the registered command manually.

  • While testing, after changing the suggested_key in manifest, you need to uninstall and re-install the extension for the changes to take effect. Simply reloading or disabling and re-enabling the extension won't work. (Thx to rsanchez for this nice catch.)

Community
  • 1
  • 1
gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • @GastonFlanchard: Did you notice my updated answer ? The last part was a bit inaccurate, so I revised that. BTW, if this answer helped you solve your problem, please **[mark it as "accepted"](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)**, so users facing a similar problem in the future will be able to spot it easily. Upvotes are also welcome :) – gkalpak Dec 24 '13 at 11:40
  • Hello, thanks for your reply! I didn't try your suggestion yet, but I will in the next days :) Then I'll mark this as accepted. Thank you again :) – Gaston Flanchard Dec 24 '13 at 19:30
  • Hi! I'm not sure to understand the purpose of this line : "var jsCodeStr = ';(' + funcToInject + ')();';" and how it really works. If you have some precious minutes to explain to me about it, it would be wonderful ;) – Gaston Flanchard Dec 27 '13 at 08:29
  • 1
    I suggest you print `jsCodeStr` to the console and see for yourself the result of the line (it is better than any explanation I could give). Basically, it takes a function (namely `funcToInject`) and converts it to a string for passing it as the value of the `code` property in `chrome.tabs.executeScript()`. Furthermore, it encloses the function in `(...)` and finally it immediately invokes it (see trailing `()`). – gkalpak Dec 27 '13 at 16:32
  • 1
    This is called an **[Immediately-invoked function expression](http://en.wikipedia.org/wiki/Immediately-invoked_function_expression)** and is equivalent to `function funcName() {...} funcName()` (but has a few benefits: easier to strignify , does not "pollute" the global `window` object etc). – gkalpak Dec 27 '13 at 16:33