2016-03-22 5 views
2

Это код из моих contentScript.js:Как вызвать функцию из вложенного скрипта?

function loadScript(script_url) 
    { 
     var head= document.getElementsByTagName('head')[0]; 
     var script= document.createElement('script'); 
     script.type= 'text/javascript'; 
     script.src= chrome.extension.getURL('mySuperScript.js'); 
     head.appendChild(script); 
     someFunctionFromMySuperScript(request.widgetFrame);// ReferenceError: someFunctionFromMySuperScript is not defined 
    } 

, но я получил сообщение об ошибке при вызове функции из введенного сценария:

ReferenceError: someFunctionFromMySuperScript не определен

Is есть способ вызвать эту функцию без изменения mySuperScript.js?

+0

http://stackoverflow.com/questions/14705593/javascript- can-not-call-content-script-js-function Вы можете взглянуть на этот вопрос. –

ответ

0

Пока функция someFunctionFromMySuperScript является глобальной, вы можете ее назвать, однако вам нужно дождаться загрузки кода.

function loadScript(script_url) 
    { 
     var head= document.getElementsByTagName('head')[0]; 
     var script= document.createElement('script'); 
     script.type= 'text/javascript'; 
     script.src= chrome.extension.getURL('mySuperScript.js'); 
     script.onload = function() { 
      someFunctionFromMySuperScript(request.widgetFrame);   
     } 
     head.appendChild(script); 
    } 

Вы также можете использовать getScript метод JQuery в.

+0

.. считая, что скрипт находится в '' web_accessible_resources'' – Xan

+0

Он не работает, someFunctionFromMySuperScript еще не определен @Xan – askona

+0

@askona Вы правы, это не сработает. Подожди немного. – Xan

1

Это не работает, потому что ваш скрипт контента и введенный сценарий live in different contexts: то, что вы вводите на страницу, находится в контексте страницы.

  1. Если вы просто хотите загрузить код динамически в контексте содержания сценария, вы не можете сделать это из скрипта содержания - вам нужно задать фоновую страницу, чтобы сделать executeScript от вашего имени.

    // Content script 
    chrome.runtime.sendMessage({injectScript: "mySuperScript.js"}, function(response) { 
        // You can use someFunctionFromMySuperScript here 
    }); 
    
    // Background script 
    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { 
        if (message.injectScript) { 
        chrome.tabs.executeScript(
         sender.tab.id, 
         { 
         frameId: sender.frameId}, 
         file: message.injectScript 
         }, 
         function() { sendResponse(true); } 
        ); 
        return true; // Since sendResponse is called asynchronously 
        } 
    }); 
    
  2. Если вам нужно вводить код в контексте страницы, то ваш метод является правильным, но вы не можете назвать это непосредственно. Используйте другие методы для связи с ним, например custom DOM events.

+0

Спасибо за ответ! Я использовал 1-й метод для запуска myScript из background.js. Но, к сожалению, myScript (который добавляет новый div в DOM) работает повсюду, кроме страницы NewTab, и только способ изменения страницы DOM NewTab делает это из сценария контента. Поэтому мне нужно выполнить myScript из контекста сценария содержимого. Это можно сделать так: ** eval (myScript.plain_code); someFunctionFromMySuperScript(); ** Это прекрасно работает, но я пытаюсь найти способ избежать использования eval. – askona

+0

Нет необходимости избегать 'eval', если единственное, что вы ему кормите, это ваш собственный файл. Eval - это зло, когда ввод недостоверен. Но опять же, я не вижу, как вы можете что-либо выполнять на странице «chrome:» - это должно быть невозможно. Вы должны изучить [переопределение страницы новой вкладки] (https://developer.chrome.com/extensions/override) вместо того, чтобы пытаться изменить существующую (хотя это не рекомендуется для небольших изменений). – Xan

+0

Более простое решение без привлечения фоновой страницы - использовать fetch или XMLHttpRequest, а затем 'window.eval'. –

2

Ваш код страдает от нескольких проблем:

  1. Как вы уже заметили, функция и переменные из нагнетаемого сценария (mySuperScript.js) не видны сценарий содержания (contentScript .js). Это потому, что два сценария работают в разных execution environments.
  2. Вставка элемента <script> со сценарием, на который ссылается атрибут src, не сразу вызывает выполнение скрипта. Поэтому, даже если скрипты должны запускаться в одной среде, вы все равно не сможете получить к ней доступ.

Чтобы решить эту проблему, сначала подумайте, действительно ли необходимо запустить mySuperScript.js на странице. Если вы не получаете доступ к каким-либо объектам JavaScript из самой страницы, вам не нужно вводить скрипт. Вы должны попытаться свести к минимуму количество кода, которое выполняется на самой странице, чтобы избежать конфликтов.

Если вы не должны запустить код на страницу, а затем запустить mySuperScript.js перед тем contentScript.js, а затем любые функции и переменные немедленно доступны (как обычно, via the manifest или programmatic injection). Если по какой-то причине сценарий действительно нужно загружать динамически, вы можете объявить его в web_accessible_resources и использовать fetch или XMLHttpRequest для загрузки сценария, а затем eval, чтобы запустить его в контексте контекста вашего контента.

Например:

function loadScript(scriptUrl, callback) { 
    var scriptUrl = chrome.runtime.getURL(scriptUrl); 
    fetch(scriptUrl).then(function(response) { 
     return response.text(); 
    }).then(function(responseText) { 
     // Optional: Set sourceURL so that the debugger can correctly 
     // map the source code back to the original script URL. 
     responseText += '\n//# sourceURL=' + scriptUrl; 
     // eval is normally frowned upon, but we are executing static 
     // extension scripts, so that is safe. 
     window.eval(responseText); 
     callback(); 
    }); 
} 

// Usage: 
loadScript('mySuperScript.js', function() { 
    someFunctionFromMySuperScript(); 
}); 

Если вы действительно должны вызвать функцию на странице из сценария (т.е. mySuperScript.js должен абсолютно выполняться в контексте страницы), то вы можете придать другой скрипт (через любой из методов от Building a Chrome Extension - Inject code in a page using a Content script), а затем передать сообщение обратно в сценарий содержимого (например, using custom events).

Например:

var script = document.createElement('script'); 
script.src = chrome.runtime.getURL('mySuperScript.js'); 
// We have to use .onload to wait until the script has loaded and executed. 
script.onload = function() { 
    this.remove(); // Clean-up previous script tag 
    var s = document.createElement('script'); 
    s.addEventListener('my-event-todo-rename', function(e) { 
     // TODO: Do something with e.detail 
     // (= result of someFunctionFromMySuperScript() in page) 
     console.log('Potentially untrusted result: ', e.detail); 
     //^Untrusted because anything in the page can spoof the event. 
    }); 
    s.textContent = `(function() { 
     var currentScript = document.currentScript; 
     var result = someFunctionFromMySuperScript(); 
     currentScript.dispatchEvent(new CustomEvent('my-event-todo-rename', { 
      detail: result, 
     })); 
    })()`; 

    // Inject to run above script in the page. 
    (document.head || document.documentElement).appendChild(s); 
    // Because we use .textContent, the script is synchronously executed. 
    // So now we can safely remove the script (to clean up). 
    s.remove(); 
}; 
(document.head || document.documentElement).appendChild(script); 

(в приведенном выше примере я использую template literals, которые поддерживаются в Chrome 41+)

Смежные вопросы