I create custom handler by extending L.Handler and add handler for map click event in addHooks method. From outer code, I`m add handler for click event, too. I use L.DomEvent.stop(e) in custom handler, but event propagate and outer code handle it. It seems like a bug.
const map = L.map("map", {
boxZoom: false
}).setView([39.50, -98.35], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
L.CustomHandler = L.Handler.extend({
initialize: function(map) {
this._map = map;
},
addHooks: function() {
this._map
.on('click', this._onMapClick, this)
},
_onMapClick: function(e) {
L.DomEvent.stop(e.originalEvent);
console.log('map click from handler');
},
removeHooks: function() {
this._map
.off('click', this._onMapClick, this);
},
});
map.addHandler('customHandler', L.CustomHandler);
map.customHandler.enable();
map.on('click', (e) => console.log('map click'));
See my plunker pls https://plnkr.co/edit/yuh67rrsOZYSpUjXam4g?p=preview
Related
Anyone know why that, when clicked, the buttons do not add or remove overlays from the map? Full PLNKR here
The HTML
<div id="toggleButtons" style="display: none">
<button id="add">Add Overlays</button>
<button id="remove">Remove Overlays</button>
</div>
The Javascript
L.Control.GroupedLayers.include({
addOverlays: function () {
for (var i in this._layers) {
if (this._layers[i].overlay) {
if (!this._map.hasLayer(this._layers[i].layer)) {
this._map.addLayer(this._layers[i].layer);
}
}
}
},
removeOverlays: function () {
for (var i in this._layers) {
if (this._layers[i].overlay) {
if (this._map.hasLayer(this._layers[i].layer)) {
this._map.removeLayer(this._layers[i].layer);
}
}
}
}
});
var control = new L.Control.GroupedLayers(ExampleData.Basemaps, {
'Landmarks': {
'Cities': ExampleData.LayerGroups.cities,
'Restaurants': ExampleData.LayerGroups.restaurants
},
'Random': {
'Dogs': ExampleData.LayerGroups.dogs,
'Cats': ExampleData.LayerGroups.cats
}
}).addTo(map);
L.DomEvent.addListener(L.DomUtil.get('add'), 'click', function () {
control.addOverlays();
});
L.DomEvent.addListener(L.DomUtil.get('remove'), 'click', function () {
control.removeOverlays();
});
And then I added the mapbox legendControl.addLegend method (from the mapbox API documentation)
map.legendControl.addLegend(document.getElementById('toggleButtons').innerHTML);
Although the buttons are shown in the map, their click properties are not working. Any clues? Thanks!
You're not 'adding' the buttons with javascript, you're making a copy of them and placing the copy into the legendControl. The actual buttons with the eventhandlers are still present in the DOM but hidden because you've added display: none as inline style. What you want to do is select the buttons and remove them from the body:
var buttons = document.getElementById('toggleButtons');
document.body.removeChild(buttons);
Then you can add them to the legend and attach the eventhandlers:
var legendControl = L.mapbox.legendControl().addTo(map);
legendControl.addLegend(buttons.innerHTML);
L.DomEvent.addListener(L.DomUtil.get('add'), 'click', function () {
control.addOverlays();
});
L.DomEvent.addListener(L.DomUtil.get('remove'), 'click', function () {
control.removeOverlays();
});
Working example on Plunker: http://plnkr.co/edit/7pDkrZbS7Re1YshKZSLs?p=preview
PS. I'm quite baffled as to why you would abuse mapbox's legend control class to add two buttons. If you need a custom control you can just create one using leaflet's L.Control class. It spares you from loading the legend control class which you're not using, thus bloat.
EDIT: As promised in the comments below an example of rolling this solution into your own custom control. I'll explain to more throughout the comments in the code but the general idea is take the basic L.Control interface and adding the functionality and DOM generation to it:
// Create a new custom control class extended from L.Control
L.Control.Toggle = L.Control.extend({
// Have some default options, you can also change/set
// these when intializing the control
options: {
position: 'topright',
addText: 'Add',
removeText: 'Remove'
},
initialize: function (control, options) {
// Add the options to the instance
L.setOptions(this, options);
// Add a reference to the layers in the layer control
// which is added to the constructor upon intialization
this._layers = control._layers;
},
onAdd: function (map) {
// Create the container
var container = L.DomUtil.create('div', 'control-overlaystoggle'),
// Create add button with classname, append to container
addButton = L.DomUtil.create('button', 'control-overlaystoggle-add', container),
// Create remove button with classname, append to container
removeButton = L.DomUtil.create('button', 'control-overlays-toggleremove', container);
// Add texts from options to the buttons
addButton.textContent = this.options.addText;
removeButton.textContent = this.options.removeText;
// Listen for click events on button, delegate to methods below
L.DomEvent.addListener(addButton, 'click', this.addOverlays, this);
L.DomEvent.addListener(removeButton, 'click', this.removeOverlays, this);
// Make sure clicks don't bubble up to the map
L.DomEvent.disableClickPropagation(container);
// Return the container
return container;
},
// Methods to add/remove extracted from the groupedLayerControl
addOverlays: function () {
for (var i in this._layers) {
if (this._layers[i].overlay) {
if (!this._map.hasLayer(this._layers[i].layer)) {
this._map.addLayer(this._layers[i].layer);
}
}
}
},
removeOverlays: function () {
for (var i in this._layers) {
if (this._layers[i].overlay) {
if (this._map.hasLayer(this._layers[i].layer)) {
this._map.removeLayer(this._layers[i].layer);
}
}
}
}
});
Now you can use your new control as follows:
// Create a new instance of your layer control and add it to the map
var layerControl = new L.Control.GroupedLayers(baselayers, overlays).addTo(map);
// Create a new instance of your toggle control
// set the layercontrol and options as parameters
// and add it to the map
var toggleControl = new L.Control.Toggle(layerControl, {
position: 'bottomleft',
addText: 'Add overlays',
removeText: 'Remove overlays'
}).addTo(map);
I know, this is quick and dirty but it should give you a decent idea of what you can do with the L.Control class in general.
Here's a working example on Plunker: http://plnkr.co/edit/7pDkrZbS7Re1YshKZSLs?p=preview
And here's the reference for L.Control: http://leafletjs.com/reference.html#control
You need to follow delegation strategy here..
document.querySelector('body').addEventListener('click', function(event) {
if (event.target.id.toLowerCase() === 'add') {
control.addOverlays();
}
if (event.target.id.toLowerCase() === 'remove') {
control.removeOverlays();
}
});
I am trying to unbind an event handler that has been added to an object's prototype. The (cut-down) code in question is:
MyClass.prototype.bindEvents = function() {
var thisObj = this;
this.$tabs.on("click", function(e) {
return thisObj.handleTabClick($(this), e);
});
}
MyClass.prototype.unbindEvents = function() {
this.$tabs.off("click", this.handleTabClick);
}
MyClass.prototype.handleTabClick = function($tab, e) {
// do something
}
I know that I can (and did) complete clear the click event by doing
this.$tabs.off("click");
but there is another event handler on there which I wish to keep.
How do I unbind a single event within the prototype structure?
You can add a namespace to the event when you create it which you can then specifically reference when you remove the event handler. try this:
MyClass.prototype.bindEvents = function() {
var thisObj = this;
this.$tabs.on("click.foo", function(e) {
return thisObj.handleTabClick($(this), e);
});
}
MyClass.prototype.unbindEvents = function() {
this.$tabs.off("click.foo");
}
For more information see the 'Event names and namespaces' section of http://api.jquery.com/on/
Also note that your method of passing the click handler through an anonymous function to the handleTabClick function is redundant, you can simply do this:
this.$tabs.on("click.foo", thisObj.handleTabClick);
MyClass.prototype.handleTabClick = function(e) {
var $tab = $(this);
// do something
}
In the following code:
var AppView = Backbone.View.extend({
events:{
"click .button":"cancel"
},
cancel:function() {
console.log("do something...");
},
onSomeEvent: function() {
this.$el.undelegate('.button', 'click', this.cancel);
}
});
var view = new AppView();
I need to undelegate this.cancel handler from elements with 'button' classes. Unfortunately this.$el.undelegate in onSomeEvent method doesn't work.
How could I remove that event handler?
try something like:
....
onSomeEvent: function() {
this.delegateEvents(
_(this.events).omit('click .button')
);
}
update:
do you mean like:
this.events[event] = "someEvent";
//call delegateEvents() on the view to re-bind events
this.delegateEvents();
I'm trying to add a mouseover event listener to a Google Maps overlay view. I've been following this question, but can't get the listener to work. This is my code:
InfoWindow.prototype = new google.maps.OverlayView;
InfoWindow.prototype.onAdd = function() {
this.getPanes().overlayMouseTarget.appendChild(this.$content.get(0));
this.getPanes().overlayMouseTarget.parentNode.style.zIndex = 100000;
google.maps.event.addListener(this, 'mouseover', function() {
console.log('MOUSEOVER');
});
};
This doesn't give any errors, but also doesn't do anything on mouseover. I've also tried using this.listeners:
this.listeners = [
google.maps.event.addDomListener(this, "mouseover", function (e) {
console.log('MOUSEOVER');
})
];
but it doesn't help either. What am I doing wrong?
For the domListener to work, you need to attach it to a dom node:
google.maps.event.addDomListener(this.$content.get(0), "mouseover", function (e) {
console.log('MOUSEOVER');
});
I'm creating a special event as described in article by Brandon Aaron here
This event needs to trigger on window resize.
$.event.special.myevent = {
setup: function () {
$(window).bind("resize", $.event.special.myevent.handler);
},
teardown: function () {
$(window).unbind("resize", $.event.special.myevent.handler);
},
handler: function (e) {
var $this = $(this);
//some other rulles
console.log("handler function");
e.type = "myevent";
jQuery.event.handle.apply(this, arguments);
}
}
binding to event:
$("#myDiv1").bind("myevent", function () {
alert("my event sub");
})
Problem:
$("#myDiv1") doesn't receive the event. This seem to be related to the fact that in setup I trigger on $(window) instead of $(this).
Question is how can I have the event that piggybacks on events happening on other objects.
You can use $.proxy to change the this reference inside the function:
setup: function () {
$(window).on("resize.myevent", $.proxy($.event.special.myevent.handler, this));
},
teardown: function () {
$(window).off("resize.myevent");
},
Fiddle
I've used an event namespace for removing the handler more easily, so you don't need a reference to the function generated by $.proxy.