How to control d3.brush (right click) - d3.js

I was wondering how to have a better control of my d3.brush component.
I would like to have some extra controls on it, like:
right click (opens a special menu, instead of regular browser one - and instead of strange brush behavior )
been able to disable the resizable (i.e. allowing the user to only move the brush and not resizing it) brush feature (whenever I want to control it)
I've already read some older similar questions here but no luck so far.
I can already read when a right click happend on my brush area with:
//code from an older post
function rightClick() {
if (d3.event.sourceEvent.which == 3 || d3.event.sourceEvent.button
== 2) { //3==firefox, 2==ie
return true;
} else {
return false;
}
}
and here is my code that uses the rightClicl() return:
function brushed() {
if(rightClick()){
console.log("Right click : " + rightClick());
}
else {
console.log("Right click <false> : " + rightClick())
x.domain(brush.empty() ? x.domain() : brush.extent());
if(!brush.empty()){
//do something and redraw it
}
else{ //correctiong when brush.empty() restables x.domain to general value
resetView();
}
}//end-of first else
}
but every time rightClick() is true, I still have my brush acting "as a regular" left-click.
Any help / insight is appreciated.
Thank you for your time.

Context menu
I'm using this d3-context-menu for showing context menu on svg elements.
However the plugin can not be used on d3.brush rects directly. The right mousedown event will also trigger brush event. I tried event.stopImmediatePropagation(), it worked.
g.selectAll(".extent")
.on("mousedown", function() {
if (d3.event.button === 2) { // only enable for right click
d3.event.stopImmediatePropagation();
}
})
.on("contextmenu", d3.contextMenu(menu));
Disable resize
It seems that d3 has no explicit option for that feature.
But you can remove the resize rects in brush group.
// after call brush
brushG.selectAll(“.resize”).remove();

Related

Hammer.js breaks vertical scroll when horizontal pan

I'm using Hammer.js to look for horizontal pan gestures, I've devised a simple function to clicks a button when panned left or right. It works okay, except the vertical scroll doesn't do anything on a touch device, or it's really glitchy and weird.
Here's the function:
var panelSliderPan = function() {
// Pan options
myOptions = {
// possible option
};
var myElement = document.querySelector('.scroll__inner'),
mc = new Hammer.Manager(myElement);
mc.add(new Hammer.Pan(myOptions));
// Pan control
var panIt = function(e) {
// I'm checking the direction here, my common sense says it shouldn't
// affect the vertical gestures, but it blocks them somehow
// 2 means it's left pan
if (e.direction === 2) {
$('.controls__btn--next').click();
// 4 == right
} else if (e.direction === 4) {
$('.controls__btn--prev').click();
}
};
// Call it
mc.on("panstart", function(e) {
panIt(e);
});
};
I've tried to add a horizontal direction to the recognizer but it didn't really help (not sure if I did it even right):
mc = new Hammer.Manager(myElement, {
recognizers: [
[Hammer.Pan,{ direction: Hammer.DIRECTION_HORIZONTAL }],
]
});
Thanks!
Try setting the touch-action property to auto.
mc = new Hammer.Manager(myElement, {
touchAction: 'auto',
recognizers: [
[Hammer.Pan,{ direction: Hammer.DIRECTION_HORIZONTAL }],
]
});
From the hammer.js docs:
When you set the touchAction to auto it doesnt prevent any defaults, and Hammer would probably break. You have to call preventDefault manually to fix this. You should only use this if you know what you're doing.
User patforna is correct. You need to adjust the touch-action property. This will fix scrolling not working when you have hammer bound on a big element in mobile.
You create a Hammer instance like so
var h = new Hammer(options.contentEl, {
touchAction : 'auto'
});
I was working on a pull to refresh feature, so I need the pan event.
Add the recognizers.
h.get( 'pan' ).set({
direction : Hammer.DIRECTION_VERTICAL,
});
h.on('panstart pandown panup panend', eventHandler);
Inside the eventhandler, you'd look at the event that was triggered and manually call on event.preventDefault() when you require it. This is applicable for hammer 2.0.6.
For anyone who's looking the pull to refresh code was taken from - https://github.com/apeatling/web-pull-to-refresh
My problem was that vertical scroll was toggling a sidebar that was supposed to show/hide on horizontal pan/swipe. After looking at the event details, I realized that Hammer probably triggers panleft and panright event based on X delta and doesn't consider Y delta, so my quick solution was to check the pan direction in my handler:
this.$data.$hammer.on('panleft', (e) => {
if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
return;
}
this.isVisible = true;
});
I was stuck on this for several days. Hope this will fix your problem.
mc = new Hammer(myElement, {
inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput,
touchAction: 'auto',
});
When the relevant gesture is triggered, we applied a css class to the element, that would set the touch-action to none.
mc.on('panmove panstart', event => {
mc.addClass('is-dragging');
}
);
.is-dragging {
touch-action: none !important;
}
Hammer 2.x does not support vertical swipe/pan. Documentation says:
Notes:
When calling Hammer() to create a simple instance, the pan and swipe recognizers are configured to only detect horizontal gestures
You can however use older 1.1.x version, which supports vertical gestures
——
Clarification: this refers to a ‘simple instance’ which is when you don’t pass in any recognizer configuration as the second parameter. In other words these are the defaults but can (and usually should) be overridden.

detecting altKey on MacOS in zoomchart

I'm implementing functionality to create a link between two nodes on Shift+Alt+Click. Like this
function graphSelectionChange(event){
var selection = event.selection;
if (selection.length === 2 && event.altKey){
var fromitem=selection[0];
var toitem=selection[1];
chart.addData({
links:[{
"id":"ll"+nextId,
from:fromitem.id,
to:toitem.id,
"style":{"label":"newLink"}
}]
});
nextId += 1;
}
}
The altKey seems not to be detected. According to this http://jsfiddle.net/Rw4km/ it is the alt/option button on a keyboard. Any clue?
Use click event (it also has selection attribute).
Selection event does not have altKey property.
There are other selection changes, like selected nodes disappearing, that do not have associated mouse clicks an you probably do not want a link added in such case.

How to change background colour of Ckeditor instance on the fly from external colour picker

I'm using some external styles on containers in our CMS, one of which is the css background colour, and then we use a CKEditor (4.x) instance to manage the content within the container. One issue is that when you want the container to have a black (#000) background but the editor has a standard white (#FFF) background and you want to use white text, you obviously wouldn't be able to see the text in the editor without MacGyvering some fake backround while you work, or working in a different colour text and then changing to white.... any way you spin it, it's a PITA and certainly not user friendly.
So what we're doing is using the change event from the colour picker in the external form field, to fire a function that changes the editor instance background on the fly. I got the base for this from a previous question (changing the background color for ckEditor):
set_background: function(hex) {
if (typeof CKEDITOR !== "object") { return; }
var editor = CKEDITOR.instances['body'];
if (editor) {
editor.on('instanceReady', function(e) {
// First time
e.editor.document.getBody().setStyle('background-color', hex);
// in case the user switches to source and back
e.editor.on('contentDom', function() {
e.editor.document.getBody().setStyle('background-color', hex);
});
});
}
}
The method is fired once on form load which works great, whatever value is in the colour picker at load is set properly and the editor inherits that background colour. However when I change the colour of the picker it doesn't update the instance. If I remove the instanceReady check however, it'll do the opposite, the load fire won't work but any changes to the picker work perfectly.
I'm guessing the ck instance isn't ready during the first call of my second scenario, meaning I probably must have it there, so my question is, how to I get the follow up calls to function properly like the first call to it does?
Thanks for any insights!
I managed to find a working solution that stores the instance once it's ready and uses that for any future calls...
ck_instance: null,
set_background: function(hex) {
if (this.ck_instance === null) {
if (typeof CKEDITOR !== "object") { return; }
this.ck_instance = CKEDITOR.instances['body'];
if (typeof this.ck_instance !== "object") { return; }
this.ck_instance.on('instanceReady', function(e) {
// First time
e.editor.document.getBody().setStyle('background-color', hex);
// in case the user switches to source and back
e.editor.on('contentDom', function() {
e.editor.document.getBody().setStyle('background-color', hex);
});
});
return;
}
// First time
this.ck_instance.document.getBody().setStyle('background-color', hex);
// in case the user switches to source and back
this.ck_instance.on('contentDom', function() {
this.ck_instance.document.getBody().setStyle('background-color', hex);
});
}
Both required firings are working now, once on form load to set the saved background in the new editor, and again by way of the colour pickers change callback.

How to detect double clicks or long clicks on points in Highcharts charts?

Highcharts offers the opportunity to detect clicks on chart points, but is it possible
to detect other events, such as the double click or mousedown event?
Thanks in advance
Each component only supports certain events, for example the Chart component will detect addSeries, click, load, redraw, and selection. I don't believe this set is extensible, so you can't capture a different event like mousedown.
You could try to inspect the source of your page and attach listeners to the elements that HighCharts generates, but this would be an undocumented work-around and would be liable to break in future releases. In addition, if you have to support < IE9 you would need handlers for both SVG and VML generated markup.
You can get creative with some events. Here's an example of detecting a double click using a click handler:
Working Demo
var clickDetected = false;
// create the chart
$('#container').highcharts({
chart: {
events: {
click: function(event) {
if(clickDetected) {
alert ('x: '+ event.xAxis[0].value +', y: '+ event.yAxis[0].value);
clickDetected = false;
} else {
clickDetected = true;
setTimeout(function() {
clickDetected = false;
}, 500);
}
}
}
},
...
It's possible, but in a different way. In Highcharts you can add event to each element using element.on. For example:
chart.series[0].data[0].graphic.on('dblclick', function() {
//callback here
});
And simple jsFiddle for you. Good thing is that you can add to all elements, and make sure work in all browsers.

jQuery click event behaves differently with live function in Firefox

Using the event click with live function leads to strange behavior when using Firefox*.
With live in Firefox, click is triggered when right-clicking also! The same does not happen in Internet Explorer 7 neither in Google Chrome.
Example:
Without live, go to demo and try right clicking
the paragraphs. A dialog menu should
appear.
With live, go to demo and try right
clicking "Click me!". Now both dialog
menu and "Another paragraph" appear.
*tested with firefox 3.5.3
As far as I know, that is a known issue (bug?). You can easily work around it by testing which button was clicked as follows:
$('a.foo').live("click", function(e) {
if (e.button == 0) { // 0 = left, 1 = middle, 2 = right
//left button was clicked
} else {
//other button was clicked (do nothing?)
//return false or e.preventDefault()
}
});
you might prefer using a switch depending on your specific requirements, but generally you would probably just want to do nothing (or or simply return) if any button other than the left button is clicked, as above:
$('a.foo').live("click", function(e) {
switch(e.button) {
case 0 : alert('Left button was clicked');break;
default: return false;
}
});
I think it's a known "bug", you could potentially query the event object after attaching the click handler ( which gets attached to the document ) and see if its a right click, otherwise manually attach the click handler after you manipulate the DOM.
After looking it up, e.button is the property you want to query:
.live('click', function(e){
if ( e.button == 2 ) return false; // exit if right clicking
// normal action
});
See my answer here: if you don't mind changing the jQuery source a bit, adding a single line in the liveHandler() works around the problem entirely.

Resources