Search code examples
firefox-addon-sdk

Listener duplication in Addon SDK panel


I am using a panel called by widget to take a snapshot of storage and display it in a table. BUt there appears to be some duplication on the markup being done.

Basically I have one storage item for testing purposes and a couple of settings like switches. Every time I click on the widget to open it the storage item gets duplicated on the page. I can understand that as the panel does not reload on show/hide.

The main issue is that I have a normal jQuery click event to send some data back to storage and this also seems to get duplicated. If I open and close the panel 6 times I get six events firing from one click.

This says to me that each reload is creating more listeners but I cannot find anything to tell me how to remove the listeners correctly.. See example

Content script

testPanel = require("sdk/panel").Panel({
    contentURL: data.url("test.html"),
    contentScriptFile: [data.url("test.js")],
    onShow: function() {
        testPanel.port.emit("Loaded")
        testPanel.port.on("clicked", function(){
            console.log('received click')
        });
    }
});

require("sdk/widget").Widget({
    id: "my-widget",
    label: "My Widget",
    panel: testPanel,
        content: "Click here"
});

test.js

var el = document.getElementsByTagName('a')[0];

el.addEventListener('click', function() {
    console.log('clicked');
    self.port.emit("clicked");
});

self.port.on("Loaded", function() {
    $(".wrapper").append('<div>New element</div>');
});

Console Output

info: test_addon: Script loaded
info: test_addon: clicked
info: test_addon: received click
info: test_addon: clicked
info: test_addon: received click
info: test_addon: received click
info: test_addon: clicked
info: test_addon: received click
info: test_addon: received click
info: test_addon: received click

Anyone had similar issues?

Problem solved

Usage of removeListener() is not 100% clear but by using a global function instead of an anonymous one you can remove the event listener onHide. It will then create a new instance onShow but the old one has been removed. Phew!

Content script

function showClick() {
    console.log('received click')
}
testPanel = require("sdk/panel").Panel({
    contentURL: data.url("test.html"),
    contentScriptFile: [data.url("test.js")],
    onShow: function() {
        testPanel.port.emit("Loaded");
        testPanel.port.on("clicked", showClick);
    },
    onHide: function() {
        testPanel.port.removeListener('clicked', showClick);
    }
});

Solution

  • I know you solved the problem but generally you don't want to create listeners in the onShow method. I'd recommend you create your listener outside of the onShow method and if you need to you can use the isShowing property to check if the panel is open when you receive the event. Now you just have a single listener for the life of the panel.

    function showClick() {
        console.log('received click')
    }
    testPanel = require("sdk/panel").Panel({
        contentURL: data.url("test.html"),
        contentScriptFile: [data.url("test.js")],
        onShow: function() {
            testPanel.port.emit("Loaded");
        },
        onHide: function() {
    
        }
    });
    testPanel.port.on("clicked", function() {
      if (testPanel.isShowing) {
        console.log('received click');
      }
    });