0

I am trying to add text to an editable field with a context menu.

I tried to follow this SO but I cannot seem to get it to add the text to the field.

This is my content, which seems to make sense. I believe it is adding the context for what the background script is looking for.

var clickedEl = null;

document.addEventListener("mousedown", function(event){
    //right click
    if(event.button == 2) {
        clickedEl = event.target;
    }
}, true);

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if(request == "getClickedEl") {
        sendResponse({value: clickedEl.value});
    }
});

And here is what I have for my Background script. This is the part where I am not sure if I am doing it correctly.

function onClickHandler(info, tab) {
  if (info.menuItemId.indexOf("context") > -1) {
    var type = info.menuItemId.replace('context', '');
    theLog = type;

    function mycallback(info, tab) {
        chrome.tabs.sendMessage(tab.id, "getClickedEl", function(clickedEl) {
            elt.value = theLog.value;
        });
    }

  }
}
Cody Smith
  • 127
  • 8

1 Answers1

1

Your background script runs in a separate hidden page with its own URL and DOM, which cannot access the web page directly, see the architecture overview in the documentation. Simply send the text to the content script, which will then use document.execCommand to insert the value into the active element.

Solution 1.

content script:

chrome.runtime.onMessage.addListener(msg => {
  document.execCommand('insertText', false, msg);
});

background script:

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId.includes('context')) {
    const text = info.menuItemId.replace('context', '');
    chrome.tabs.sendMessage(tab.id, text, {frameId: info.frameId || 0});
  }
}

Note we're sending directly to the frame where the context menu was invoked, which is needed in the general case (maybe not in yours) with the content script running in all iframes which is declared in manifest.json:

"content_scripts": [{
  "matches": ["<all_urls>"],
  "all_frames": true,
  "match_about_blank": true,
  "js": ["content.js"]
}]

Solution 2.

However, if this is the only function of the content script, it's better not to declare it in manifest.json at all, but instead inject dynamically in the background script:

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId.includes('context')) {
    const text = info.menuItemId.replace('context', '');
    chrome.tabs.executeScript(tab.id, {
      frameId: info.frameId || 0,
      matchAboutBlank: true,
      code: `document.execCommand('insertText', false, ${JSON.stringify(text)})`,
    });
  }
}

And add the permission in manifest.json that doesn't require a user confirmation on installation (documentation):

"permissions": ["activeTab"]
wOxxOm
  • 65,848
  • 11
  • 132
  • 136