I'm seeing some strange behavior in d3. I have a force directed graph in the usual way
node = svg.selectAll(".node").data(graph.nodes).enter().append("circle")....
and when someone clicks on a node, I want a simple animation (instead of a console.log)
function set_focus(d) { console.log('set'); }
function remove_focus() { console.log('remove'); }
node.on("mousedown", set_focus);
node.on("mouseup", remove_focus);
Interestingly, when I mousedown on a node, the set event fires, but when I release the mouse, remove_focus doesn't fire. Anyone have any idea what's going on?
d3v5, chrome 65, macOS 10.13
tldr: the zoom event is conflicting with your mouseup event.
I created a fork because I'm not too sure what you're going to want to do with this and I didn't want to mess with your git history: this link is the fork.
It seems that your Zoom event on the SVG is "consuming" all other events once the mouse is clicked. My impression is that this is a known and commonly complained about issue. I think that this GitHub ticket is related, and so is this one. Per Mike Bostock,
This is the expected behavior. Per the release notes: “The zoom
behavior now consumes handled events.” So, if a mouseup event was part
of a zoom gesture, then the zoom behavior stops the immediate
propagation of that mouseup event preventing other listeners from
receiving it (and where possible prevents the associated browser
default behavior). This makes it easier to combine zooming and
dragging as in this example:
http://bl.ocks.org/mbostock/3127661b6f13f9316be745e77fdfb084
If you want to prevent a zoom gesture from starting, use zoom.filter
to ignore certain events, or use event.stopPropagation or
event.stopImmediatePropagation to prevent the zoom behavior from
receiving the initiating event. If you want to do something after a
zoom gesture, listen to the zoom event.
I can get your mouseup event to fire by either deleting the d3.zoom event, or by calling d3.event.stopPropagation within the set_focus function. So, the two lines below (and in the fork) get your remove_focus function to fire, but I'm not sure if I also messed up how you want the zoom event to be working:
function set_focus(d) {
console.log('set');
d3.event.stopPropagation();
node.style("opacity", function(o) { return (d == o || o.layer == d.layer - 1) ? 1 : 0.1; });
link.style("opacity", function(o) { return (o.target == d.id) ? 1 : 0.02; });
}
function remove_focus() {
console.log('remove');
d3.event.stopPropagation();
node.style("opacity", 1);
link.style("opacity", function () { return edgeOpacity; })
}
Now you have a listener for the end of a zooming event see here
so you could do something like
d3.zoom().on('end', remove_focus)
Related
I try to get the onSelectionChange event for ckeditor.
When you select something it ckeditor rise correctly the event, but if you select something else in the same paragraph the event is not raised.
Is there a way to make creditor rise onSelectionChange event any time i select some text. ?
Any advice on solve this issue ?
Thanks
The answer is in the API documentation:
Fired when selection inside editor has been changed. Note that this event is fired only when selection's start element (container of a selecion start) changes, not on every possible selection change. Thanks to that selectionChange is fired less frequently, but on every context (the elements path holding selection's start) change.
You can observe all mouse and keyboard actions though (JSFiddle):
function logSelection() {
console.log( this.getSelection() );
}
CKEDITOR.replace( 'editor', {
on: {
contentDom: function() {
this.document.on( 'mouseup', logSelection, this );
this.document.on( 'keyup', logSelection, this );
}
}
} );
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.
In trying to figure out how to start and stop Sprite animations from a SpriteSheet, I tried this:
// other code...
// define animations in SpriteSheet:
"animations": {"intro": [0, 19, "false"]}
// other code...
var spriteSheet = new createjs.SpriteSheet(data);
var intro = new createjs.Sprite(spriteSheet, "intro");
stage.addChild(intro);
createjs.Ticker.addEventListener("tick", stage);
intro.stop();
var btnStart = document.getElementById("btnStart");
btnStart.onclick = function() {
console.log("btnStart clicked");
intro.on("animationend", onStartAnimEnd);
intro.play();
};
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
console.log("Start anim ended");
}
In the above example, if the user clicks the btnStart button, the onStartAnimEnd callback fires indefinitely, even though by defining "false" in the animation configuration to signal we want to stop on the last frame, and the animation does in fact stop, and I'm removing the event listener in the first line of the callback.
If I add:
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
intro.stop();
console.log("Start anim ended");
}
The problem goes away, but that doesn't seem right... So, if I change the listener assignment of the animationend event from:
intro.on("animationend", onStartAnimEnd);
to:
ask.addEventListener("animationend", onAskAnimEnd);
...and with this change, the indefinite event captures goes away. So my questions are:
What's the difference with these two event listener assignments?
Is this animationend event continually firing in the background because we're updating the stage on the tick event, even though nothing needs re-rendering?
Thanks for your time!
This is actually a duplicated question. As I answered you previous question, your animation definition is wrong, you need to use the boolean value (false) and not the string value ("false").
Now sure what ask is, but the method on is a wrapper for addEventListener, and where you can specify things such as the scope of the callback and if it will run only once. Take a look at the API to know more:
http://www.createjs.com/Docs/EaselJS/classes/EventDispatcher.html#method_on
I am using the Alloy Diagram Builder to create and display network topology.
I would like to remove default click and drag events attached to each nodes, so viewers would not have the ability "build" diagrams but only view diagrams that I have generated.
http://alloyui.com/examples/diagram-builder/real-world/
I have tried these but it does not work.
// detach click event to all nodes with class aui-diagram-node.
Y.all('.aui-diagram-node').detach("click");
// unbind
$(".aui-diagram-node").each(function(){
$(this).unbind();
});
I believe the event is attached to the container .aui-diagram-builder-drop-container via delegate() and the event would be mousedown.
Merely by accident I found a hack that might work for this. I was adding tooltips to my page on which I had a diagram builder, well apparently the tooltips layer a div over the page and simply set the opacity on it to be clear and the object still resides. After a tooltip had come up i was unable to interact with the piece of the diagram builder the tooltip had popped up over.
So based of this concept, why not try overlaying a div over the entire canvas of the diagram and give it a high z-index so that it sits on top. It should effectively not allow interaction with the canvas.
Yes it's a kludge but it just may work.
To make a DiagramBuilder read-only, you can detach() events from all of its children recursively:
/*
* Readonly the diagram
*/
function ReadonlyDiagram(diagram) {
function detachRecursively(node) {
node.get('children').each(detachRecursively);
// You may also want to set the cursor to the default since it will
// change based on which elements the mouse is over.
// node.setStyle('cursor', 'auto');
// You may want to detach specific events such as 'click' or
// 'mousedown' if you do not want to disable all events.
node.detach();
};
diagram.on('render', function (event) {
detachRecursively(diagram.get('boundingBox'));
});
}
Now, you must be post diagramBuilder object to ReadonlyDiagram function like below codes:
YUI().use('aui-diagram-builder', function (y) {
var diagram = new y.DiagramBuilder(
{
availableFields: data,
boundingBox: '#' + containerId,
fields: nodes,
srcNode: '#' + builderId
}).render();
diagram.connectAll(connections);
if (callBackDiagram !== undefined) callBackDiagram(diagram);
if(isReadonly === true) ReadonlyDiagram(diagram);
});
});
Reference
As the title says. I want to make a list of div elements inside a div. I want to be able to scroll the list up and down, and when the list is not scrolling anymore, i want to be able to click the listed elements do to something. I cant figure out how to do this.
the touchmove event executes whenever the user touches the div, even if the div its scrolling. THen i cant figure out how to make let the program know that the div isnt scrolling anymore, so the next touch on the elements will trigger a non scrollable event.....
EDIT:
what i have so far is this... However this is a quick "fix" and its not working as intended. For example if you scroll quickly up and down, then the div will think you pressed on one of the elements..
exerciseWrapper is the elements inside the scrolling div. Each element is wrapped around exerciseWrapper.
$('.exerciseWrapper').on('touchstart',function(){
touchstart=true;
setTimeout(function(){
touchstart=false;
}, 100);
});
$('.exerciseWrapper').on('touchend',function(){
if(touchstart)
{
$('.exerciseWrapper').not(this).css('border-color','black');
$(this).css('border-color','orange');
}
});
Ok so i finally figured this one out.. Reason why i couldnt wrap my minds around the solution on this, was because i couldnt get other events then eventstart and event end to work... At the time of writing i still cant get the other events like eventcancel and eventleave to work. However eventmove works and i solved the problem using this event. eventmove keeps updating the element its linked when you move your finger. Then i had a variable of touchmove to constantly be true if i am scrolling my div (with touchmove event). WHen i stop moving i can clik on selected element again and use a normal eventstart event.. This is my working code:
var touchmove=false;
function AddExercisesEvents()
{
$('#exerciseMenu').on('touchmove',function(){
touchstart=true;
$('h1').text("move");
});
$('.exerciseWrapper').on('touchend mouseup',function(event){
event.stopPropagation();
event.preventDefault();
// $('.exerciseWrapper').off();
if(event.handled !== true)
{
touchstart=false;
//ENTERING editExercise Menu..!
if(!touchstart)
{
//insert magic here
alert($(this).attr('id'));
}
}
return false;
});
}