Listener duplication in Addon SDK panel - firefox-addon-sdk

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);
}
});

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');
}
});

Related

Leaflet - How to add click event to button inside marker pop up in ionic app?

I am trying to add a click listener to a button in a leaftlet popup in my ionic app.
Here I am creating the map & displaying markers, also the method I want called when the header tag is clicked is also below:
makeCapitalMarkers(map: L.map): void {
let eventHandlerAssigned = false;
this.http.get(this.capitals).subscribe((res: any) => {
for (const c of res.features) {
const lat = c.geometry.coordinates[0];
const lon = c.geometry.coordinates[1];
let marker = L.marker([lon, lat]).bindPopup(`
<h4 class="link">Click me!</h4>
`);
marker.addTo(map);
}
});
map.on('popupopen', function () {
console.log('Popup Open')
if (!eventHandlerAssigned && document.querySelector('.link')) {
console.log('Inside if')
const link = document.querySelector('.link')
link.addEventListener('click', this.buttonClicked())
eventHandlerAssigned = true
}
})
}
buttonClicked(event) {
console.log('EXECUTED');
}
When I click this header, Popup Open & Inside if are printed in the console, so I know I'm getting inside the If statement, but for some reason the buttonClicked() function isn't being executed.
Can someone please tell me why this is the current behaviour?
I just ran into this issue like 2 hours ago. I'm not familiar with ionic, but hopefully this will help.
Create a variable that keeps track of whether or not the content of your popup has an event handler attached to it already. Then you can add an event listener to the map to listen for a popup to open with map.on('popupopen', function(){}). When that happens, the DOM content in the popup is rendered and available to grab with a querySelector or getElementById. So you can target that, and add an event listener to it. You'll have to also create an event for map.on('popupclose', () => {}), and inside that, remove the event listener from the dom node that you had attached it to.
You'd need to do this for every unique popup you create whose content you want to add an event listener to. But perhaps you can build a function that will do that for you. Here's an example:
const someMarker = L.marker(map.getCenter()).bindPopup(`
<h4 class="norwayLink">To Norway!</h4>
`)
someMarker.addTo(map)
function flyToNorway(){
map.flyTo([
47.57652571374621,
-27.333984375
],3,{animate: true, duration: 5})
someMarker.closePopup()
}
let eventHandlerAssigned = false
map.on('popupopen', function(){
if (!eventHandlerAssigned && document.querySelector('.norwayLink')){
const link = document.querySelector('.norwayLink')
link.addEventListener('click', flyToNorway)
eventHandlerAssigned = true
}
})
map.on('popupclose', function(){
document.querySelector('.norwayLink').removeEventListener('click', flyToNorway)
eventHandlerAssigned = false
})
This is how I targeted the popup content and added a link to it in the demo for my plugin.
So yes you can't do (click) event binding by just adding static HTML. One way to achieve what you want can be by adding listeners after this new dom element is added, see pseudo-code below:
makeCapitalMarkers(map: L.map): void {
marker.bindPopup(this.popUpService.makeCapitalPopup(c));
marker.addTo(map);
addListener();
}
makeCapitalPopup(data: any): string {
return `` +
`<div>Name: John</div>` +
`<div>Address: 5 ....</div>` +
`<br/><button id="myButton" type="button" class="btn btn-primary" >Click me!</button>`
}
addListener() {
document.getElementById('myButton').addEventListener('click', onClickMethod
}
Ideally with Angular, we should not directly be working with DOM, so if this approach above works you can refactor adding event listener via Renderer.
Also I am not familiar with Leaflet library - but for the above approach to work you need to account for any async methods (if any), so that you were calling getElementById only after such DOM element was successfully added to the DOM.

I can't get click event working in CKEditor after switching to Source mode and back

I set event handler inside the iframe of a CKEdtor as below:
CKEDITOR.on('instanceReady', function() {
$('.cke_contents iframe').contents().click(function() {
alert('Clicked!');
});
});
It works well, but when I click 'Source' button, it doesn't work any more (alert not working).
Someone can help me???
Please use below code:
var editor = CKEDITOR.replace( 'editor1', { });
editor.on("pluginsLoaded", function( event ) {
editor.on( 'contentDom', function( ) {
var editable = editor.editable();
editable.attachListener( editable, 'click', function( evt ) {
console.log('click' );
}, null, null, 10 );
} );
} );
You need to use contentDom event if you want to keep click listener when switching to Source mode and back - https://docs.ckeditor.com/ckeditor4/docs/#!/api/CKEDITOR.editor-event-contentDom.
Please note that you should attach the listener to editable instead of iframe and you don't need jQuery for that.

Double on click event with mapbox gl

I am redrawing layers on style.load event and removing the layers
map.on('style.load', function() {
loadByBounds(tempBounds)
});
function loadByBounds(b) {
if (map.getLayer("cluster-count")) {
map.removeLayer("cluster-count");
}
...
map.on('click', 'unclustered-point', function(e) {
var popup = new mapboxgl.Popup()
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(text)
.addTo(map);
})}
But how to remove map.on('click') events? As when I click the point the Popup() displays 2 times. And when I change layer one more time the onclick event fires 3 times and so on. So I think I have to remove the click event but how? Thanks
You might wanna use map.once(). This will add a listener that will be called only once to a specified event type. However after 1 click event got fired this event listener won't listen to any further click events.
https://www.mapbox.com/mapbox-gl-js/api/#evented#once
With map.off() it's basically the opposite of map.on() and you can use it to unregister any applied event listeners. However you would need to add event listeners without an anonymous function in order to use map.off().
https://www.mapbox.com/mapbox-gl-js/api/#map#off
// you would need to use a named function
function clickHandler(e) {
// handle click
}
map.on('click', clickHandler);
// then you can use
map.off('click', clickHandler);
// With an anonymous function you won't be able to use map.off
map.on('click', (e) => {
// handle click
});
To prevent your app from registering multiple listeners you maybe need to set a flag that gets set after your first event listener got applied.
let notListening = true;
function loadByBounds(b) {
// ....
if (notListening) {
notListening = false;
map.on('click', (e) => {
// do something
});
}
}

On-demand popup in a Crossrider extension

I am looking for a way to display an on-demand pop in a Crossrider extension, in the same fassion as the Javascript 'confirm' dialog. I have an HTML page in the resources and I would like to use it in a popup, which will be displayed whenever a certain message is dispatched. I realize there is functionality to display popups in Crossrider (appAPI.browserAction.setPopup), however I would like to display a custom popup on-demand, instead of a simple JS 'confirm' dialog. Is there a way of doing that? Thank you.
You can use a combination of appAPI.browserAction.setPopup and appAPI.browserAction.clearPopup to control the popup. In the following example, the extension scope code determines which popup is required based on the page visited and sets it accordingly:
extension.js:
appAPI.ready(function($) {
if (appAPI.isMatchPages("*google", "*msn")) {
// Send message to background to set the popup based on the page url
appAPI.message.toBackground({
request: 'set-popup',
popup: (location.hostname.indexOf('google') !== -1)
? 'google.html'
: 'msn.html'
});
} else {
// Send message to background to clear the popup for other pages
appAPI.message.toBackground({
request: 'clear-popup'
});
}
});
background.js:
appAPI.ready(function($) {
appAPI.browserAction.setResourceIcon('icon.jpg');
appAPI.message.addListener(function(msg) {
switch(msg.request) {
case 'set-popup':
// When setting the page, first clear the existing popup
appAPI.browserAction.clearPopup();
// then set the new popup
appAPI.browserAction.setPopup({
resourcePath: msg.popup,
width: 300,
height: 200
});
break;
case 'clear-popup':
appAPI.browserAction.clearPopup();
break;
}
});
});
[Disclosure: I am a Crossrider employee]

webworks blackberry 10 window.open twitter facebook

i am just trying to implement facebook and twitter in my Webworks App and cannot get them work together.
I am using the FaceBook-OAuth-2 and the Twitter-OAuth-1 sample and i just put both stuff together and my problem is that only the first startOAuth() opens a window in the app to login the second doesn't so if i first clicked facebook it works after when i try twitter nothing happens.
https://github.com/blackberry/BB10-WebWorks-Samples
thanks
function setClickHandlers() {
console.log('set click handlers');
var fb = document.getElementById('facebookOn');
fb.addEventListener('click', function(e) {
// if the childWindow is already open, don't allow user to click the button
if(childWindow !== null) {
return false;
}
e.preventDefault();
toast('Contacting Facebook...');
setTimeout(function() {
startOAuth();
}, 500);
});
console.log('set twitter click handlers');
var tw = document.getElementById('twitterOn');
tw.addEventListener('click', function(e) {
// if the childWindow is already open, don't allow user to click the button
if(childWindow !== null) {
return false;
}
e.preventDefault();
toast('Fetching access token...');
setTimeout(function() {
twittergetAccessToken();
}, 500);
});
}
I would start by adding some debug code in your click handler to see if that's getting called when you click the button in the first place.
If it is, then I recommended you use Web Inspector (console) to see if there are any errors. If there are, they'll show up there.
Good reference for Web Inspector here - http://developer.blackberry.com/html5/documentation/web_inspector_overview_1553586_11.html
If the click handler is not being fired then perhaps you have the wrong element ID, or the setClickHandlers function is not being executed.