我如何制作一个外观和行为都像切换的浏览器操作按钮

How I can make a browser action button that looks and acts like a toggle

本文关键字:按钮 操作 浏览器 何制作 外观 一个      更新时间:2023-09-26

目标是获取一个 Firefox 的 WebExtension,它可以由工具栏中的用户激活/停用,就像开/关开关一样。

我正在使用背景.js带有以下代码:

browser.browserAction.onClicked.addListener(function (tab) {
    switch (button) {
        case 'turn-on':
            enable();
            break;
        case 'turn-off':
            disable();
            break;
    }
});
function enable() {
    browser.browserAction.setIcon({ path: '/ui/is-on.png', });
    browser.browserAction.setPopup({ popup: '/ui/turn-off.js', });
    browser.webNavigation.onCommitted.addListener(onTabLoad); 
} 
function disable() {
    browser.browserAction.setIcon({ path: '/ui/is-off.png', });
    browser.browserAction.setPopup({ popup: '/ui/turn-on.js', });
    browser.webNavigation.onCommitted.removeListener(onTabLoad); 
}
function onTabLoad(details) {
    browser.tabs.executeScript(details.tabId, {
        file: '/gc.js',
        allFrames true,
    }); 
}
enable(); // enable or disable by default

显然我做错了什么。我是编码新手。这是我正在尝试完成的个人项目。

你的代码

虽然你添加了一个switch语句来打开button,但你从未定义button,也没有改变它的状态。您也没有默认情况,以防万一 button 变量不是您在 case 语句中测试的值之一。

不应使用browserAction.setPopup()设置弹出窗口。 设置弹出窗口将导致弹出窗口被打开,而不是您的后台页面接收click事件。此外,弹出窗口必须是 HTML 页面,而不是 JavaScript。

请参阅下面的部分,了解您需要在onTabLoad()中解决的Firefox错误。

侦听webNavigation.onCommitted不足以涵盖何时需要注入脚本的所有情况。换句话说,webNavigation.onCommitted不会在每次加载页面时触发。要完全涵盖需要注入脚本的每种情况,您需要在另一个问题中提出。

var nextButtonState;
browser.browserAction.onClicked.addListener(function (tab) {
    switch (nextButtonState) {
        case 'turn-on':
            enable();
            break;
        case 'turn-off':
        default:
            disable();
            break;
    }
});
function enable() {
    browser.browserAction.setIcon({ path: '/ui/is-on.png', });
    //browser.browserAction.setPopup({ popup: '/ui/turn-off.js', });
    browser.webNavigation.onCommitted.addListener(onTabLoad); 
    nextButtonState = 'turn-off';
} 
function disable() {
    browser.browserAction.setIcon({ path: '/ui/is-off.png', });
    //browser.browserAction.setPopup({ popup: '/ui/turn-on.js', });
    browser.webNavigation.onCommitted.removeListener(onTabLoad); 
    nextButtonState = 'turn-on';
}
function onTabLoad(details) {
    //Add a setTimout to avoid a Firefox bug that Firefox is not quite ready to 
    //  have tabs.executeScript() inject a script when the onCommitted event fires.
    setTimeout(function(){
        chrome.tabs.executeScript(details.tabId, {
            file: '/gc.js',
            allFrames true,
        }); 
    },0);
}
enable(); // enable or disable by default

火狐浏览器webNavigation.onCommitted错误的解决方法

需要对onTabLoad()代码进行更改,以便在 Firefox 中使用webNavigation.onCommitted侦听器通过tabs.executeScript()注入脚本(Chrome 中不需要(。这是由于 Firefox 中的一个错误,如果立即从webNavigation.onCommitted侦听器执行,会导致tabs.executeScript()失败。 我使用的解决方法是在setTimeout(function,0)延迟后注入脚本。 这允许 Firefox 执行所需的代码来设置executeScript()正常运行所需的环境。

function onTabLoad(details) {
    //Add a setTimout to avoid a Firefox bug that Firefox is not quite ready to 
    //  have tabs.executeScript() inject a script when the onCommitted event fires.
    setTimeout(function(){
        chrome.tabs.executeScript(details.tabId, {
            file: '/gc.js',
            allFrames true,
        }); 
    },0);
}

多状态按钮(例如切换按钮(的通用解决方案

我用来使浏览器操作按钮的行为类似于切换的代码如下。我修改了 browserButtonStates 对象,它描述了按钮的功能和外观,以添加和删除webNavigation.onCommitted侦听器,onTabLoad() .有关onTabLoad()的问题,请参阅上文。

下面的代码比您需要的代码更复杂。 我编写它的目的是能够将其从一个项目移动到另一个项目,只需要更改browserButtonStates对象的内容。然后,只需更改该对象,就可以更改图标、文本、徽章文本、徽章颜色和在每种状态(例如开/关(中执行的操作。

背景.js

//The browserButtonStates Object describes the states the button can be in and the
//  'action' function to be called when the button is clicked when in that state.
//  In this case, we have two states 'on' and 'off'.
//  You could expand this to as many states as you desire.
//icon is a string, or details Object for browserAction.setIcon()
//title must be unique for each state. It is used to track the state.
//  It indicates to the user what will happen when the button is clicked.
//  In other words, it reflects what the _next_ state is, from the user's
//  perspective.
//action is the function to call when the button is clicked in this state.
var browserButtonStates = {
    defaultState: 'off',
    on: {
        icon         : '/ui/is-on.png'
        //badgeText  : 'On',
        //badgeColor : 'green',
        title        : 'Turn Off',
        action       : function(tab) {
                           chrome.webNavigation.onCommitted.removeListener(onTabLoad);
                       },
        nextState    : 'off'
    },
    off: {
        icon         : '/ui/is-off.png'
        //badgeText  : 'Off',
        //badgeColor : 'red',
        title        : 'Turn On',
        action       : function(tab) {
                           chrome.webNavigation.onCommitted.addListener(onTabLoad);
                       },
        nextState    : 'on'
    }
}
//This moves the Browser Action button between states and executes the action
//  when the button is clicked. With two states, this toggles between them.
chrome.browserAction.onClicked.addListener(function(tab) {
    chrome.browserAction.getTitle({tabId:tab.id},function(title){
        //After checking for errors, the title is used to determine
        //  if this is going to turn On, or Off.
        if(chrome.runtime.lastError){
            console.log('browserAction:getTitle: Encountered an error: ' 
                + chrome.runtime.lastError);
            return;
        }
        //Check to see if the current button title matches a button state
        let newState = browserButtonStates.defaultState;
        Object.keys(browserButtonStates).some(key=> {
            if(key === 'defaultState') {
                return false;
            }
            let state = browserButtonStates[key];
            if(title === state.title) {
                newState = state.nextState;
                setBrowserActionButton(browserButtonStates[newState]);
                if(typeof state.action === 'function') {
                    //Do the action of the matching state
                    state.action(tab);
                }
                //Stop looking
                return true;
            }
        });
        setBrowserActionButton(browserButtonStates[newState]);
    });
});
function setBrowserActionButton(tabId,details){
    if(typeof tabId === 'object' && tabId !== null){
        //If the tabId parameter is an object, then no tabId was passed.
        details = tabId;
        tabId       = null;
    }
    let icon   = details.icon;
    let title  = details.title;
    let text   = details.badgeText;
    let color  = details.badgeColor;
    //Supplying a tabId is optional. If not provided, changes are to all tabs.
    let tabIdObject = {};
    if(tabId !== null && typeof tabId !== 'undefined'){
        tabIdObject.tabId = tabId;
    }
    if(typeof icon === 'string'){
        //Assume a string is the path to a file
        //  If not a string, then it needs to be a full Object as is to be passed to
        //  setIcon().
        icon = {path:icon};
    }
    if(icon) {
        Object.assign(icon,tabIdObject);
        chrome.browserAction.setIcon(icon);
    }
    if(title) {
        let detailsObject = {title};
        Object.assign(detailsObject,tabIdObject);
        chrome.browserAction.setTitle(detailsObject);
    }
    if(text) {
        let detailsObject = {text};
        Object.assign(detailsObject,tabIdObject);
        chrome.browserAction.setBadgeText(detailsObject);
    }
    if(color) {
        let detailsObject = {color};
        Object.assign(detailsObject,tabIdObject);
        chrome.browserAction.setBadgeBackgroundColor(detailsObject);
    }
}
//Set the starting button state to the default state
setBrowserActionButton(browserButtonStates[browserButtonStates.defaultState]);

manifest.json

{
    "description": "Demo Button toggle",
    "manifest_version": 2,
    "name": "Demo Button toggle",
    "version": "0.1",
    "background": {
        "scripts": [
            "background.js"
        ]
    },
    "browser_action": {
        "default_icon": {
            "32": "myIcon.png"
        },
        "default_title": "Turn On",
        "browser_style": true
    }
}