AmCharts, stop a 'clickGraphItem' event propagating to 'clickGraph' - javascript

I am using AmCharts and would like to know how to stop the clickGraphItem event propagating to a clickGraph event.
The reason I want to stop it is this:
When I click on a bubble in an XY chart I use Mustache to render a little form under the graph with info contained in the dataContext of the clicked item.
As there are many bubbles on the plot I would like to be able to highlight the selected item on the bubble chart after it is clicked so as to let the user know what point the info is being edited for.
However if the user clicks in empty space on the graph (the clickGraph event on it's own) I want to fire event.chart.validateData(); so that the clicked bubble goes back to original colors.
The problem I am encountering is that when I click on the item I get first the clickGraphItem event firing but that also triggers the clickGraph event.
I have tried to put event.event.stopPropagation() at the end of the "Render Mustache and change fill of bubble" method but this does nothing.
The function I am using to handle both events is below:
function showPointForm(event) {
var ev = event
if (ev.item && ev.item.dataContext.data != undefined) {
var bullet = ev.item.bulletGraphics.node;
bullet.setAttribute("stroke",colors.info);
bullet.setAttribute("fill",colors.warning);
$(document).find('#form-area').html(
Mustache.render($(document).find('#form-template').html(),{
data : ev.item.dataContext.data
})
)
}else{
$(document).find('#form-area').html('')
event.chart.validateData();
}
ev.event.stopPropagation()
}
var chart = AmCharts.makeChart('div',configObject)
chart.addListener("clickGraphItem",showPointForm);
chart.addListener("clickGraph",showPointForm);
The idea here is that if the event is from an item, it will change the fill of the bullet and the stroke, and then render a form with some data prop from the item. If the event does not have an item and data, the rendered form get whiped and the chart is redrawn to original styles.
The stopping of event propagation does nothing here to stop the clickGraph event being fired
Any ideas on what I am doing wrong here?
Thanks
SOLUTION:
It turns out that what I was attempting to achieve isn't supported through the standard AmCharts API. The clickGraph event will not fire in empty space on a bubble/XY chart. Instead I attached an event in to the click event of the .amcharts-plot-area.
function showPointForm(event) {
var ev = event
if (ev.item && ev.item.dataContext.data != undefined) {
$(document).find('.highlight').each(function(){
$(this).attr('class', 'amcharts-graph-bullet');
})
var bullet = ev.item.bulletGraphics.node;
bullet.setAttribute("class",'highlight amcharts-graph-bullet');
$(document).find('#form-area').html(
Mustache.render($(document).find('#form-template').html(),{
data : ev.item.dataContext.data
})
)
}else{
$(document).find('#form-area').html('')
event.chart.validateData();
}
}
function reset(){
$(document).find('#form-area').html('')
timelineChart.validateData();
}
var chart = AmCharts.makeChart('div',configObject)
chart.addListener("clickGraphItem",showPointForm);
$(document).on("click",'.amcharts-plot-area',reset);
So now when I click on a bubble, all bubbles that were previously styled with the highlight class have that class removed. Then the clicked bubble has it added.
When there is a click event in the amcharts-plot-area however, the reset() function is called which removes all the highlighted classes again.
This solves my issue but as stated below the stopping of event propagation is not possible.

There isn't a way to prevent chart event propagation. The chart event name is passed in the type property, so you can use that to determine which flow you want. It's not pretty, but that's all you can do:
function showPointForm(event) {
if (event.type == "clickGraphItem") {
// logic for clickGraphItem only
}
else {
// logic for clickGraph
}
}
You could also set a custom flag inside the chart when the clickGraphItem is fired to indicate that it was just triggered so you know not to perform any additional logic if it was bubbled, for example:
function clickGraphHandler(event) {
if (event.type == "clickGraphItem") {
event.chart.clickGraphItemFired = true;
console.log('clickGraphItem')
} else {
if (event.chart.clickGraphItemFired) {
event.chart.clickGraphItemFired = false;
console.log('clickGraph - bubbled from clickGraphItem');
} else {
console.log('clickGraph - not bubbled from clickGraphItem')
}
}
}
Demo

Related

Google Maps API v3: allow default context menu on custom OverlayView

I've got a map with custom overlays (based on https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/overlay-popup).
The custom overlay content includes a link/anchor tag and I would like to allow the user to right click the link and select "Open in new tab", however right clicks are cancelled by the map and I am unable to figure out how to prevent that behaviour.
If you compare the sample of the custom overlay linked above with the default Info Window https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/infowindow-simple you can notice that the custom overlay does NOT show the context menu when right clicking on the "Hello World" text, whereas the Info Window DOES show the context menu. In the dev tools I noticed an event handler on the Info Window that somehow allows the context menu (removing that handler stops the context menu coming up), however as it's in the minified Google maps code I'm not able to make sense of it.
I have tried the following:
google.maps.event.addListener(map, 'rightclick', function (e) {
var event = e.ya;
var element = event.target;
if (element.nodeName === "A") {
event.stopImmediatePropagation();
event.stopPropagation();
return true;
}
});
Code is executed, but there's still no context menu. Instead it breaks something on the map as the map then moves with the mouse as if I still had the mouse down (looks like I prevented the mouseup handler).
I also tried to set preventMapHitsFrom on the custom overlay (https://developers.google.com/maps/documentation/javascript/reference/overlay-view#OverlayView.preventMapHitsAndGesturesFrom), which made the above not fire any more, but still no context menu.
I was also able to attach an event handler myself (excuse the jQuery):
$(document).on("contextmenu", ".map-popup__link", function (e) {
e.stopImmediatePropagation();
return true;
});
But again not sure how to prevent the event being cancelled. I also tried to trigger a new event on the same element, but that just creates a loop (obviously) without solving the problem.
Based on https://stackoverflow.com/a/7414594/1397352
I have modified the Popup.prototype.onAdd function to
Popup.prototype.onAdd = function () {
this.getPanes().floatPane.appendChild(this.containerDiv);
this.getPanes().overlayMouseTarget.appendChild(this.containerDiv);
// set this as locally scoped var so event does not get confused
var me = this;
// Add a listener - we'll accept clicks anywhere on this div, but you may want
// to validate the click i.e. verify it occurred in some portion of your overlay.
google.maps.event.addDomListener(this.containerDiv, 'contextmenu', function () {
google.maps.event.trigger(me, 'contextmenu');
});
};
Breakpoint in event handler gets hit, but again no context menu shows.
Has anyone got this to work with custom overlays?
Thanks to MrUpsidown's comment I was able to find a solution in the (archived) infobox library: https://github.com/googlemaps/v3-utility-library/blob/master/archive/infobox/src/infobox.js#L231
It looks like I was close in my first attempt, but should have set event.cancelBubble = true;
Final solution:
Popup.prototype.onAdd = function () {
this.getPanes().floatPane.appendChild(this.containerDiv);
// This handler allows right click events on anchor tags within the popup
var allowAnchorRightClicksHandler = function (e) {
if (e.target.nodeName === "A") {
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
}
};
this.contextListener_ = google.maps.event.addDomListener(this.containerDiv, "contextmenu", allowAnchorRightClicksHandler);
};
Popup.prototype.onRemove = function () {
if (this.contextListener_) {
google.maps.event.removeListener(this.contextListener_);
this.contextListener_ = null;
}
if (this.containerDiv.parentElement) {
this.containerDiv.parentElement.removeChild(this.containerDiv);
}
};
see https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/javascript/examples/overlay-popup for remainder of Popup code

Kendo UI Chart disable legend item right click event

I have an application that uses a Kendo UI Chart with a legend. When the user clicks on a legend item, the Kendo onLegendItemClick(e) method gets called. However the event that gets passed to this function does not contain the originalEvent, so there is no way to distinguish between right and left clicks.
Here is the relevant API reference: https://docs.telerik.com/kendo-ui/api/javascript/dataviz/ui/chart/events/legenditemclick
I tried adding an event listener and capturing the 'mousedown' event before onLegendItemClick is invoked as shown below. However, this approach will fail on touch screen devices (iPads, tablets, mobile devices, etc).
document.addEventListener("mousedown", saveMouseDown, true);
function saveMouseDown(ev) {
$scope.mouseDownEvent = ev;
}
$scope.$on("$destroy", function () {
document.removeEventListener(saveMouseDown);
});
The application has a separate directive for handling right clicks. Is there a way to prevent Kendo from calling the onLegendItemClick(e) method when a user right clicks the legend item?
Add "click" to the list of saved events1:
document.addEventListener("mousedown click", saveEvent, true);
function saveEvent(ev) {
$scope.savedEvent = ev;
}
$scope.$on("$destroy", function () {
document.removeEventListener(saveEvent);
});
Related question: How to prevent right click from deselecting marker in Kendo-UI

Regaining focus in iFrame with jQuery context menu

I've created a fiddle to reproduce the problem.
https://jsfiddle.net/rvwp47Lz/23/
callback: function (key, option) {
console.log("You clicked the test button", this);
// Need the iframe contents to regain focus so the mouse events get caught
setTimeout(function () {
$iframe[0].contentWindow.focus();
}, 100);
}
Basically, what I want to happen is the mouse move events to be caught after closing the context menu.
I can call focus on the iFrame's body or document but it doesn't seem to have any effect.
After you right click one of the items within the iframe and select an item, the mousemove event on the iframes body is no longer called (you can also notice that the hover CSS effect on the items are no longer working).
Ideas?
After some debugging and playing around with jQuery.contextMenu's code it seems the issue actually comes from the itemClick function. I added comments to the code and will add an issue to their github for a possible fix (unless there's some reason they're disabling default here)
// contextMenu item click
itemClick: function (e) {
var $this = $(this),
data = $this.data(),
opt = data.contextMenu,
root = data.contextMenuRoot,
key = data.contextMenuKey,
callback;
// abort if the key is unknown or disabled or is a menu
if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-submenu, .context-menu-separator, .' + root.classNames.notSelectable)) {
return;
}
// This line is causing the issue since it's preventing the default actions which puts
// mouse events back into place. Chrome must disable mouse move events when the contextmenu event
// gets triggered to improve performance.
//e.preventDefault();
e.stopImmediatePropagation();

Assigning click event handler inside keyup event causes the click event to be fired multiple times

I am attempting to add an event handler to an anchor only when certain form fields are populated, like so:
$('#newName, #newFrom').keyup(function (e) {
if ($('#newName').val() || $('#newFrom').val()) {
$('#add-person').click(function (e) {
//Handle event, includes adding a row to a table.
$('this').off();
});
}
});
It seems like the first event is getting propagated to the second one since I end up with the same number of rows in my table as keys I have typed.
I've tried adding
e.stopPropagation();
But with no success.
$('this').off(); should be $(this).off();
also probably you'd better go using the input event instead of keyup. input event will trigger even if one pastes content into your fields.
nevertheless I'd go the other way around:
// (cache your selectors)
var $newName = $("#newName"),
$newFrom = $("#newFrom");
// create a boolean flag
var haveNewValue = false;
// modify that flag on fields `input`
$newName.add( $newFrom ).on("input", function() {
haveNewValue = ($.trim($newName.val()) + $.trim($newFrom.val())).length > 0;
});
// than inside the click test your flag
$('#add-person').click(function (e) {
if(!haveNewValue) return; // exit function if no entered value.
// do stuff like adding row to table
});
What was wrong:
on every keyup you was assigning a new (therefore multiple) click event/s to the button, but the (corrected to:) $(this).off() was triggered only after an actual button click.
Also a better way to use .on() and off.() (notice the difference in using the .click() method and the .on() method) is:
function doCoffee() {
alert("Bzzzzzzzz...BLURGGUZRGUZRGUZRG");
}
$("#doCoffeeButton").on("click", doCoffee); // Register "event" using .on()
$("#bossAlertButton").click(function() {
$("#doCoffeeButton").off("click"); // Turn off "event" using .off()
});

Make virtual earth infobox appear on click instead of hover?

Is it possible to make Virtual Earth pushpin infobox display respond from onclick instead of mouseover?
Currently I'm using something like this:
...
var pin = new VEShape(
VEShapeType.Pushpin,
[location]
);
pin.SetCustomIcon(icon_url);
pin.SetTitle(title);
pin.SetDescription(info_window_template);
map.AddShape(pin);
document.getElementById(pin.GetPrimitive().iid).onmouseover = EventHandlerOnMouseOver;
}
var EventHandlerOnMouseOver = function(e) {
// Handle content loading...
}
...
However, if I attempt to change the onmouseover to onclick, VE picks up onclick and disables the infobox entirely.
You can accomplish what describing through the use of events... note that I'm using Virtual Earth 6.2.
The trick is to suppress the onmouseover event and the subscribe to the onclick event. When you have figured out if the user clicked on a shape or not you can call the ShowInfoBox method on the map control to force it to show the info box.
And here is teh codez =)
// Begin by supressing the onmouseover event. Returning true
// from an event handler disables the default Virtual Earth action
map.AttachEvent('onmouseover', function(e) { if(e.elementID) return true; });
// Subscribe to the onclick event and react whenever we get an element id
map.AttachEvent("onclick", function(e) {
// elementID is null when someone clicks on the map and not on a shape
if(e.elementID) {
var shape = map.GetShapeByID(e.elementID);
if(shape) map.ShowInfoBox(shape);
}
});
Note that the infobox will show even if you right click on the shape; to avoid this you would look at the leftMouseButton or rightMouseButton properties on the event object.
References:
VEMap.onclick Event
VEMap.onmouseover Event

Categories

Resources