Javascript Can';t将API连接到Chrome扩展

Javascript Can';t将API连接到Chrome扩展,javascript,jquery,google-chrome,google-chrome-extension,Javascript,Jquery,Google Chrome,Google Chrome Extension,我正在开发chrome扩展。我想在点击popup.html中的按钮后,将一些API连接到当前选项卡。我在popup.js中使用此代码: $('button').click(function() { chrome.tabs.executeScript({ file: 'js/ymaps.js' }, function() {}); }); 在ymaps.js中,我使用以下代码将API连接到当前选项卡: var script = document.createEle

我正在开发chrome扩展。我想在点击
popup.html
中的按钮后,将一些API连接到当前选项卡。我在
popup.js
中使用此代码:

$('button').click(function() {
    chrome.tabs.executeScript({
        file: 'js/ymaps.js'
    }, function() {});
});
ymaps.js
中,我使用以下代码将API连接到当前选项卡:

var script = document.createElement('script');
script.src = "http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU";
document.getElementsByTagName('head')[0].appendChild(script);
使用Yandex映射需要此API。所以,在代码之后,我创建了地图应该放置的位置:

$('body').append('<div id="ymapsbox"></div>');
我想,一切都很清楚,如果你还在阅读,我会解释问题所在。 当我点击我的
popup.html
中的按钮时,我进入了Chrome的控制台
未捕获的引用错误:未定义ymap
。似乎api库未连接。但是当我手动输入console
ymap
时,我会得到可用方法的列表,所以库已连接。那么,当我从executed
.js
-文件调用
ymap
-对象时,为什么会出现这样的错误呢

UPD:我还尝试将
ymap.ready(init)
包装到
$(document.ready()
函数中:

$(document).ready(function() {
    ymaps.ready(init);
})
但错误仍在出现。 下面的人说api库可能还没有加载。但这段代码也会产生错误

   setTimeout(function() {
        ymaps.ready(init);
    }, 1500);
我甚至试着这样做

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
    if (changeInfo.status == "complete") {
        chrome.tabs.executeScript({
            file: 'js/gmm/yandexmaps.js'
        });
    }
});

这不是一个时间问题,而是一个与“执行环境”相关的问题

将脚本注入网页的JS上下文(将脚本标记插入
head
),但尝试从内容脚本的JS上下文调用
ymap
。然而,内容脚本“生活”在一个孤立的世界中,无法访问网页的JS上下文(请看

编辑(感谢Rob的评论)

通常,您可以捆绑库的副本,并将其作为内容脚本注入。在您的特定情况下,这也不会有帮助,因为库本身会将脚本标记插入到中以加载依赖项


可能的解决方案: 根据您的具体要求,您可以:

  • 您可以在弹出窗口或新选项卡中显示地图(并让用户与之交互),而不是将地图插入网页。您将在包含库的新窗口/选项卡中提供要加载的HTML文件(参考文件的捆绑副本或在之后使用CDN-建议使用前者)

  • 修改外部库(即消除插入脚本标记)。我建议不要这样做,因为这种方法会带来额外的维护“成本”(例如,每次更新库时,您都需要重复此过程)

  • 将所有代码注入网页上下文。
    可能的陷阱:弄乱网页JS,例如覆盖已定义的变量/函数。 此外,如果您需要与chrome.*API交互,此方法将变得越来越复杂(这对于网页的JS上下文不可用,因此您需要设置专有的消息传递机制,例如使用自定义事件)

  • 然而,如果您只需要执行一些简单的初始化代码,这是一个可行的替代方案:

    例如:

    ymap.js:

    function initMap() {
        ymaps.ready(init);//Waits DOM loaded and run function
        var myMap;
        function init() {
            myMap = new ymaps.Map("ymapsbox", {
                center: [55.76, 37.64],
                zoom: 7
            });
        }
    }
    
    $('body').append('<div id="ymapsbox"></div>');
    var script1 = document.createElement('script');
    script1.src = 'http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU';
    script1.addEventListener('load', function() {
        var script2 = document.createElement('script');
        var script2.textContent = '(' + initMap + ')()';
        document.head.appendChild(script2);
    });
    document.head.appendChild(script1);
    
    函数initMap(){
    ready(init);//等待加载DOM并运行函数
    var-myMap;
    函数init(){
    myMap=newymaps.Map(“ymapsbox”{
    中间:[55.76,37.64],
    缩放:7
    });
    }
    }
    $('body')。追加('');
    var script1=document.createElement('script');
    script1.src=http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-汝';
    脚本1.addEventListener('load',function(){
    var script2=document.createElement('script');
    var script2.textContent='('+initMap+')()';
    文件.头.子文件(脚本2);
    });
    文档.头.子文档(脚本1);
    
    Rob已经指出了这个主题的重要资源:

    ymap
    未定义,因为您试图在内容脚本中使用它,而库是在页面上下文中加载的(通过
    标记)

    通常,您可以通过将库作为内容脚本加载来解决此问题,例如

    chrome.tabs.executeScript({
    文件:“library.js”
    },函数(){
    chrome.tabs.executeScript({
    文件:“yourscript.js”
    });
    });
    
    但是,这并不能解决您的问题,因为您的库在
    标记中加载了更多的外部脚本。因此,库的一部分仅对网页中的脚本可见(而对内容脚本不可见,因为它们是独立的)

    解决方案1:截取
    标记并将其作为内容脚本运行。 从中获取
    scriptTagContext.js
    ,并在其他内容脚本之前加载它。此模块通过将
    (在内容脚本中创建)的执行环境更改为内容脚本来解决您的问题

    chrome.tabs.executeScript({
    文件:“scriptTagContext.js”
    },函数(){
    chrome.tabs.executeScript({
    文件:“js/ymaps.js”
    });
    });
    
    有关文档,请参阅。
    有关解决方案背后的概念的解释,请参见

    解决方案2:在页面的上下文中运行 如果您不知何故不想使用前面的解决方案,那么还有另一个选项可以让代码运行。我强烈建议不要使用这种方法,因为它可能(并且会)在其他页面中引起冲突。然而,为了完整性:

    在页面上下文中运行所有代码,方法是通过页面中的
    标记插入内容脚本(或至少是使用外部库的扩展部分)。这只有在您不使用任何Chrome扩展API的情况下才有效,因为您的脚本将在网页的有限权限下有效运行

    例如,您问题中的代码将按如下方式重新构造:

    var script=document.createElement('script');
    script.src=”http://api-maps.yandex.ru/2.0-stable/?load=packa
    
    function initMap() {
        ymaps.ready(init);//Waits DOM loaded and run function
        var myMap;
        function init() {
            myMap = new ymaps.Map("ymapsbox", {
                center: [55.76, 37.64],
                zoom: 7
            });
        }
    }
    
    $('body').append('<div id="ymapsbox"></div>');
    var script1 = document.createElement('script');
    script1.src = 'http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU';
    script1.addEventListener('load', function() {
        var script2 = document.createElement('script');
        var script2.textContent = '(' + initMap + ')()';
        document.head.appendChild(script2);
    });
    document.head.appendChild(script1);