【问题标题】:Completely lost on how to save extension popup window content完全迷失了如何保存扩展弹出窗口内容
【发布时间】:2017-09-05 13:27:50
【问题描述】:

每次打开新链接或单击“离开”链接时,如何使弹出窗口中添加的内容不会消失,我对此非常困惑。我已经阅读了有关内容脚本、后台脚本等的内容,但我真的不知道如何将其实现到我自己的源代码中。下面是我的 popup.htmlpopup.js 和我的 manifest.js 文件。

{
    "manifest_version": 2,
    "name": "URL_save",
    "description": "This extension saves an URL and renames the title to the user's wishes and hyperlink the title.",
    "version": "0.1",

    "browser_action": {
        "default_icon": "/img/icon.png",
        "default_popup": "popup.html",
        "default_title": "See your saved websites!"
    },

    "permissions": [
        "tabs"
    ]
}

弹出 html

<html>
  <head>
    <title>Your articles</title>
    <link href="/css/style.css" rel="stylesheet"/>
    <script src="/js/underscore-min.js"></script>
    <script src="/js/popup.js"></script>
  </head>
  <body>
    <div id="div">No content yet! Click the button to add the link of the current website!</div>
    <div><ul id="list"></ul></div>
    <br/>
    <button id="button">Add link!</button>
  </body>
</html>

popup.js

// global variables
var url;

// event listener for the button inside popup window
document.addEventListener('DOMContentLoaded', function() {
    var button = document.getElementById('button');
    button.addEventListener('click', function() {
        addLink();
    });
});

// fetch the URL of the current tab, add inside the window
function addLink() {
// store info in the the queryInfo object as per: 
//   https://developer.chrome.com/extensions/tabs#method-query
    var queryInfo = {
    currentWindow: true,
    active: true
    };

    chrome.tabs.query(queryInfo, function(tabs) {
        // tabs is an array so fetch the first (and only) object-elemnt in tab
        // put URL propery of tab in another variable as per: 
        //   https://developer.chrome.com/extensions/tabs#type-Tab
        url = tabs[0].url;

        // format html
        var html = '<li><a href=' + url + " target='_blank'>" + url + '</a><br/></li>';

        // change the text message
        document.getElementById("div").innerHTML = "<h2>Saved pages</h2>";

        // get to unordered list and create space for new list item 
        var list = document.getElementById("list");
        var newcontent = document.createElement('LI');
        newcontent.innerHTML = html;

        // while loop to remember previous content and append the new ones
        while (newcontent.firstChild) {
            list.appendChild(newcontent.firstChild);
        }
    });
}

在这张图片中,您可以看到当我第一次添加链接但随后(仅)关闭弹出窗口并再次打开它时会发生什么:

添加当前网址后:

关闭并重新打开弹窗后:

【问题讨论】:

  • 目前这个问题有点过于宽泛,无法提供完整的代码答案。有一些选择(例如,如果您只想存储此会话的 URL,跨 Chrome 关闭/重新启动,或跨使用此配置文件的所有计算机)来限制答案集,以便将其降低到特定的关于如何存储和恢复数据的答案。

标签: javascript html google-chrome google-chrome-extension firefox-addon-webextensions


【解决方案1】:

类似于网页,弹出窗口(或选项/设置页面)的范围在显示时创建,在不再可见时销毁。这意味着在弹出窗口显示的时间之间没有存储状态。您希望在弹出窗口被销毁后保留的任何信息,您都需要存储在其他地方。因此,您将需要使用 JavaScript 来存储您希望在下次打开弹出窗口时保持相同的任何状态。每次打开弹出窗口时,您都需要检索该信息并将其恢复到 DOM。最常用的两个位置是StorageAreaMDN,或背景页面。

您将信息存储在哪里取决于您希望存储的数据保留多长时间以及您希望在哪里看到数据。

您可以存储数据的一般位置包括(存在其他可能性,但以下是最常见的):

  • 如果您希望数据仅在 Chrome 关闭之前存在,则为后台页面。一旦 Chrome 重新启动,它就不会存在。您可以通过几种/几种不同的方法将数据发送到后台页面,包括message passingMDN,或directly changing values on the background page@ 987654326@。存储在 StorageArea(以下两个选项)中的数据也可用于后台页面和内容脚本。
  • chrome.storage.localMDN 如果您希望数据在 Chrome 关闭并重新启动时保留在本地计算机上。
  • chrome.storage.syncMDN 如果您希望与使用当前 Chrome 帐户/配置文件的所有 Chrome 实例共享数据。数据也将保留直到更改。它可以通过关闭并重新启动 Chrome 来使用。它将在使用相同配置文件的其他机器上可用。
  • window.localStorage:在chrome.storage 存在之前,将扩展数据存储在window.localStorage 中很流行。虽然这仍然有效,但通常首选使用 chrome.storage

使用chrome.storageStorageAreaMDN 的优点之一是数据可直接用于扩展程序的所有部分,而无需将数据作为消息传递。1

您当前的代码

目前,您的代码不会存储在弹出窗口的 DOM 之外的任何地方输入的 URL。您需要建立一个数据结构(例如一个数组)来存储 URL 列表。然后可以将这些数据存储到上述存储位置之一。

Google 在Options documentation page2、MDN 上的示例显示了在显示选项页面时存储chrome.storage.sync 并将值恢复到 DOM。此示例中用于选项页面的代码可以通过将其 HTML 页面定义为 default_popupbrowser_action 来完全按原样用于弹出窗口。还有许多其他可用的示例。

不幸的是,如果您没有详细说明您想要什么,就很难为您提供具体的代码。但是,有几个建议可以朝着您需要的方向前进:

  • 重构您的代码,以便您拥有一个单独的函数,您可以使用 URL 作为参数调用它只是将此 URL 添加到您在 DOM 中的列表中(例如addUrlToDom(url))。当用户添加 URL 以及页面加载时恢复 URL 时,将使用此功能。
  • 将您的 URL 列表存储在一个数组中(例如 urlList)。该数组将是您保存到弹出窗口之外的存储位置的内容。您将从DOMContentLoaded 处理程序中的该存储位置读取此数组,并使用重构的addUrlToDom() 函数添加每个值。将其恢复到 DOM 中可能类似于:

    urlList.forEach(function(url){
        addUrlToDom(url);
    });
    

将您的数据存储在chrome.storage.local

假设您想在 Chrome 关闭/重启期间将 URL 存储在本地计算机上(即使用 chrome.storage.local),您的代码可能类似于:

manifest.json 仅更改为 permissions

    "permissions": [
        "tabs",
        "storage"
    ]

popup.js

// global variables
var urlList=[];

document.addEventListener('DOMContentLoaded', function() {
    getUrlListAndRestoreInDom();
    // event listener for the button inside popup window
    document.getElementById('button').addEventListener('click', addLink);
});

// fetch the URL of the current tab, add inside the window
function addLink() {
    chrome.tabs.query({currentWindow: true,active: true}, function(tabs) {
        // tabs is an array so fetch the first (and only) object-element in tab
        var url = tabs[0].url;
        if(urlList.indexOf(url) === -1){
            //Don't add duplicates
            addUrlToListAndSave(url);
            addUrlToDom(url);
        }
    });
}

function getUrlListAndRestoreInDom(){
    chrome.storage.local.get({urlList:[]},function(data){
        urlList = data.urlList;
        urlList.forEach(function(url){
            addUrlToDom(url);
        });
    });
}

function addUrlToDom(url){
    // change the text message
    document.getElementById("div").innerHTML = "<h2>Saved pages</h2>";

    //Inserting HTML text here is a bad idea, as it has potential security holes when
    //  including content not sourced entirely from within your extension (e.g. url).
    //  Inserting HTML text is fine if it is _entirely_ sourced from within your
    //  extension.
    /*
    // format HTML
    var html = '<li><a href=' + url + " target='_blank'>" + url + '</a></li>';
    //Add URL to DOM
    document.getElementById("list").insertAdjacentHTML('beforeend',html);
    */
    //Build the new DOM elements programatically instead:
    var newLine = document.createElement('li');
    var newLink = document.createElement('a');
    newLink.textContent = url;
    newLink.setAttribute('href',url);
    newLink.setAttribute('target','_blank');
    newLine.appendChild(newLink);
    document.getElementById("list").appendChild(newLine);
}

function addUrlToListAndSave(url){
    if(urlList.indexOf(url) === -1){
        //URL is not already in list
        urlList.push(url);
        saveUrlList();
    }
}

function saveUrlList(callback){
    chrome.storage.local.set({urlList},function(){
        if(typeof callback === 'function'){
            //If there was no callback provided, don't try to call it.
            callback();
        }
    });
}

  1. 例外情况是您插入到页面上下文中的脚本。页面上下文是您可能不会在其中运行脚本的内容。为此,您必须使用内容脚本(您的 StorageAreaMDN 数据直接位于可用)将&lt;script&gt; 标记插入网页的DOM。这可能有点复杂,您可能不需要担心它。这里提到它仅仅是因为StorageAreaMDN 数据可用于您的扩展的所有区域的声明可能存在例外。
  2. Chrome 文档中的示例在 Firefox 上运行良好。是的,Firefox supports both chrome.*, using callbacks, and browser.*, using promises

【讨论】:

  • 哇...毫无疑问,你真的很棒。你真是个天才。我需要了解更多信息,感谢您为我指出正确的方向,甚至给了我更好的代码!我感谢您的帮助,您对我的帮助远远超过了我所能(并且会)要求的!我真的很高兴,谢谢!
  • 但是我确实有一个问题,在最后一个函数“saveUrlList”中,它将回调作为参数。但在“addUrlToListAndSave”函数中,“saveUrlList”不接收任何参数。回调函数不是定义如下:“嘿(无论名称)函数,当你完成工作时给我打电话并(可选地)给我你工作的输出”。然后前面的函数,比如 chrome.storage.local.set 调用了无名函数。但我不明白为什么需要最后一个函数以及它是如何工作的。在developer.chrome.com/extensions/storage 上我觉得有点模糊。
  • 是的,回调通常是一个函数 (CB),它被传递给另一个函数 (S),目的是在 S 应该执行的操作完成后调用 CB。在这段代码中,saveUrlList() 能够接受一个 optional 回调,一旦 urlList 被保存,就会调用该回调。使用if(typeof callback === 'function'){ 测试可以看到回调函数作为callback 传递。如果没有传递任何函数,那么我们就不会尝试调用它。 addUrlToListAndSave 保存完成后没有什么必须做的,所以没有传递回调函数。 (续)
  • (cont') 可以删除saveUrlList() 中的整个if 语句块。当前功能不需要它,但将来可能需要。在代码中拥有该功能主要是为了在任何其他代码中需要时允许它(例如,我在答案中链接的选项示例使用set() 回调来通知用户选项已更改)。可能需要也可能不需要使用回调,但是当您实现从列表中删除 URL 的方法时,您可能希望使用 saveUrlList()(这本身并不需要该功能)。
  • 我明白了,谢谢!我现在几乎理解了所有代码。但是我没有得到关于 developer.chrome 网站的 chrome.storage.local.set 的这句话:“一个对象,它提供每个键/值对来更新存储。存储中的任何其他键/值对都不会受到影响。”它们是否意味着(在我们的上下文中)当您提供一个对象(内部是 urlList 数组)时,如果其中的 url 已经 存在 ,则不会将其设置到存储中?我也没有得到: "chrome.storage.local.get( {urlList : [] }, ...." 第二个函数的一部分。为什么数组必须在对象内?
【解决方案2】:

每次关闭/重新打开时,弹出窗口都会重新加载它的文档。相反,您应该使用背景页面来保留状态。

执行此操作的一般方法是:

  1. 从弹出窗口到后台进行通信以执行某些操作。即:执行 ajax 请求并将结果存储在普通的 javascript 对象中。
  2. 从后台页面获取,获取javascript对象。即使您关闭弹出窗口,状态也会保留在后台页面中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多