dc.js: cannot scroll past chart even after setting mouseZoomable to false - d3.js

Background
I have created a chart and implemented a click-to-zoom/mouseout-to-reset-zoom feature as follows.
chart.on('pretransition', (c) => {
// click to enable zooming
c.select('svg').on('click.enablemousezoomable', () => {
c.focus();
c.mouseZoomable(true).render();
});
chart.on('postRedraw', (c) => {
// mouseleave to disable zooming
c.select('svg').on('mouseleave.disablemousezoomable', () => {
c.focus();
c.mouseZoomable(false).render();
});
});
Problem
While the feature works as intended, the problem is that after redrawing, page scrolling is impossible if the cursor remains on the chart.
The cursor needs to be moved off of the chart in order to get page scrolling to work.
Question
What might be causing this problem and what is the solution?
Thanks!

I made a block from the dc.js stock example, which uses mouseZoomable in the Monthly Index Abs Move & Volume/500,000 Chart, and applied your changes.
Indeed, it disables zoom but it still leaves the wheel disabled after mouseZoomable has been disabled.
As dc.js#991 discusses, the way dc.js removes zoom is incorrect. According to the d3-zoom documentation, this should be correct:
moveChart._nullZoom = function(sel) {
sel.on('.zoom', null);
};
Indeed, I tried it in this fork of the block, and it seems to work much better. I think it's the correct fix for the linked issue, too.
There are some artifacts due to the redraws for adding and removing zoom, but I think they are out of scope for this question.

Related

Tooltip issue in amchart v4 , not reload on pie chart slice

series.slices.template.events.on("over", function(ev) {
series.slices.template.tooltipHTML=showHtml(ev, series.name);
}, this);
Tooltip issue in amchart4: not reload on pie chart slice.
I am using custom tooltip like generating custom html on mouse hover. Everything is working but when I move cursor from one slice to another, the popup html is not refreshing. When I mouse out and then again mouse over the same slice, the popup html shows correct data.
I think the previous html data is cached somewhere. Please help me.
It could possibly help to see what showHtml is doing. But we'll make do without that.
There are a few problems with this code.
Code-wise, the most obvious is this line:
series.slices.template.tooltipHTML=showHtml(ev, series.name);
You're resetting the template's tooltipHTML instead of the actual slice's tooltipHTML.The template has applyOnClones as true by default so it will propagate to all the other slices, so if this worked it was by accident. If there's any slice-specific settings in showHtml that don't rely on data placeholders this will break tooltipHTML for the next slice that's hovered over.
The real issue, however, is the approach.
So long as tooltipHTML or tooltipText are set, a tooltip will appear on hover.
It's better to use an adapter for tooltipHTML in this case.
As a quick test to see which wins the race condition, the hover event or the tooltipText/HTML adapter, make a handler for each, and hover over a slice:
series.slices.template.events.on("over", function(){
console.log("hover");
});
// override tooltipText so tooltipHTML is actually used
series.slices.template.tooltipHTML = "something...";
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML) {
console.log("adapter");
return tooltipHTML;
});
// console:
// "adapter"
// "hover"
You'll find that the adapter triggers first, so by the time you hover, the tooltip is already on its way with its HTML and all.
So use an adapter, just be sure to adjust your showHtml function so its first argument takes target itself instead of event (it may not be necessary to have another argument with the tooltipHTML that's being modified because it will always be sent the original tooltipHTML unformatted string, so that could be referred to manually whether as a string or variable).
The adapter can look like this:
var originalTooltipHTML = "<strong>{country}: </strong>"
series.slices.template.tooltipHTML = originalTooltipHTML;
series.slices.template.adapter.add("tooltipHTML", function(tooltipHTML, target) {
// #2: If we had used an event, here you would work on the target itself, event.target, not the template.
// The showHtml might not even be needed, perhaps whatever it does can go in here instead.
return showHtml(target);
});
Here's a demo with all that thrown together:
https://codepen.io/team/amcharts/pen/a0122e572d27cf513a78384345cad3a6

CKEditor "overflow: scroll" on parent causes toolbar to freeze at initial position

When you add a CKEditor to a div inside a div with: "overflow: scroll" the toolbar won't move when scrolling the parent div...
<div id="wrapper" style="overflow: scroll;">
<div contenteditable="true">This is the ckedito</div>
</div>
An example can be found here: ​http://jsfiddle.net/W8Dt4/
Does anyone know a workaround around this problem?
I think the desired behaviour would be:
Keep the toolbar at the top of the editor div when there's enough room.
Move the toolbar to the bottom of the editor div when there's not enough room on top and there is enough room on the bottom.
Using version 4.4.3, I was able to solve this problem by fire the window scroll event in a similar way that it is done in other areas in CKEditor. Attach a scroll event to the parent container that has overflow:scroll; set on it and trigger the window scroll within. The positioning is a little clunky but still works.
$("#parent-with-scroll").on('scroll', function (evt) {
CKEDITOR.document.getWindow().fire('scroll');
});
Yup. CKEditor never considered such case and, most likely, editor will never deal with such edge case.
Still, what you need is a scroll listener for editor.element.getParent() in those lines of floatingspace plugin. Unfortunately, you have to wait for the ticket #9816 to be resolved, because it changes the way of re-positioning the toolbar, and makes your case possible to fix. Once the fix is released (in 4.2.1), you got to basically change the lines to look like this:
var elementParent = editor.element.getParent();
editor.on( 'focus', function( evt ) {
...
elementParent.on( 'scroll', uiBuffer.input );
} );
editor.on( 'blur', function() {
...
elementParent.removeListener( 'scroll', uiBuffer.input );
} );
editor.on( 'destroy', function() {
...
elementParent.removeListener( 'scroll', uiBuffer.input );
} );
If you want, you can give it a try with this ticket branch. Otherwise, you got to need to wait for the upcoming release to patch your code.
There's also one thing that you need to know: each floating toolbar is appended to <body>, so it will never belong to the same (overflowed) container enclosing your editor. Even though the toolbar will scroll along with the container, it will always float above it and there's not much you can do about it unless you also hack this line. Note that I haven't tested it.
I hope this helped you.

Disable 2 finger swipe back navigation in Chrome on OSX [duplicate]

I want to disable the two finger swipe that causes Chrome going back or forward.
I have a website where the user might lose progress on his work if he doesn't specifically saves.
I have tried using window.onbeforeunload but that doesn't seem to work if I have hashes in the url (back forward would change between www.example.com/work/#step1#unsaved www.example.com/work/#step0) and the event doesn't seem to trigger.
I was about to switch to another solution but today I noticed that in Google Docs it's completely disabled. How did they achieve that?
Disable Chrome two fingers back/forward swipe
This worked for me. In your CSS file:
html {
overscroll-behavior-x: none;
}
body {
overscroll-behavior-x: none;
}
Make the specific page open in a new tab/window by default (by putting target="_blank"> in hyperlink). That way there'll be no previous page to go back to.
Or prevent Horizontal Scrolling by default. To do that, you can use jquery.mousewheel to do:
$(document).on("mousewheel",function(event,delta){
// prevent horizontal scrolling
if (event.originalEvent.wheelDeltaX !== 0) {
event.preventDefault();
}
});
Assuming you have a horizontal-scrolling element, adding overscroll-behavior-x: contain; is the easiest way prevent the scroll action from spilling out into the page and causing the navigation.
https://dev.to/danburzo/css-micro-tip-prevent-history-navigation-on-horizontally-scrolling-elements-3iil
https://caniuse.com/#feat=css-overscroll-behavior
Disable or replace swipe gestures for Google Chrome 61
The question that leads me here was marked "duplicate" and closed to answers. I believe this answer is better suited for the "duplicated" question, however, I feel this answer could possibly save time for someone landing on either question.
Better question:
Disable navigation swipe on Chrome browser in javascript
This Google developers article helped me to allow the e.preventDefault() to work and prevent swipe gestures as of Chrome 61.
https://developers.google.com/web/updates/2017/01/scrolling-intervention
givanse's answer to the following was the code that I used to write my own swipe event handlers:
Detect a finger swipe through JavaScript on the iPhone and Android
In summary, the following two events are used to implement the swipe gestures:
handleTouchStart (e) {
...
},
handleTouchMove (e) {
...
e.preventDefault()
}
As of Chrome 56, the default behavior is to make the event listeners passive and thus disable the ability to prevent Chrome's swipe gestures. To override this behavior, event listeners can be added as follows:
document.addEventListener(
'touchstart',
this.handleTouchStart,
{passive: false}
)
document.addEventListener(
'touchmove',
this.handleTouchMove,
{passive: false}
)
By passing the {passive: false} object as the third parameter to the addEventListener method, the listener is registered as active and can stop Chrome's default behavior with the e.preventDefault() event method.
Building on both the previous answers given by #roy riojas and #redgetan - I combined their answers to allow for this to be dynamic and prevent both forward and backwards - again - per #roy's comments - you must know the class of your element, and for this implementation - the class of the nested element that is actually being scrolled
(function ($) {
$(document).on('mousewheel', function(e) {
var $target = $(e.target).closest('.scrollable-h');
var scroll = $target.scrollLeft();
var maxScroll = $target.find('.scrollable-h-content').width() - $target.width();
if(scroll <= 0) {
if(scroll <= 0 && e.originalEvent.wheelDeltaX >= 0) {
e.preventDefault();
}
}
if(scroll >= maxScroll) {
if (scroll >1 && e.originalEvent.wheelDeltaX <= 0) {
e.preventDefault();
}
}
});}(jQuery));
I've been working on something similar where I want to override the forward/backward history swiping gesture. Depending on what your swipe area is you can tweak the selector as follows:
html { touch-action:none; }
This is the associated documentation that gives you all the properties to all touch actions like panning or zooming features built into the browser.
https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action
I was able to disable it by typing chrome://flags in the address bar and heading down to "Overscroll history navigation" and setting it to "Disabled" from the dropdown.
You can disable back/forward with this code:
document.addEventListener("wheel", function(event) {
event.preventDefault();
}, { passive: false });
Note that adding { passive: false } is essential, at least in Chrome. If you only want to disable back/forward in certain areas you can use code like this (assuming you're using jquery and you add the class disable-back-forward to the sections where you want to disable back/forward):
document.addEventListener("wheel", function(event) {
if ($(event.target).closest('.disable-back-forward').length)
event.preventDefault();
}, { passive: false });
Hi this worked for me on chrome but not for the entire page, but for places where I have scrollable content.
In Google Docs (Spreadsheets) it seems to be working because they don't have a back page to go. If you navigate to another URL (manually) it will not prevent you from navigating back.
$(document).on('mousewheel', function(e) {
var $target = $(e.target).closest('.scrollable-h');
if ($target.scrollLeft () <= 4) {
$target.scrollLeft(5);
return false;
}
});
One thing to keep in mind is that the code above is making two assumptions:
your element with horizontal scrollable content has a class scrollable-h
If checks if the scrollLeft if bigger less than 4px and then just make it scroll to 5px
returning false effectively cancel the back gesture
Important:
- This only prevents the back swipe gesture, when is done fast, if you do it very slow it will still trigger sometimes.
Also this does not prevent the forward swipe gesture, but it could also be done by checking if the element has reached the maximum scrollLeft. If that is the case then move it 20px back and return false to prevent the event from happening... It is up to you to add this use case if it happens to make sense to you.
You can take a look to a proof of concept here. http://jsfiddle.net/royriojas/JVA6m/#base
You're looking at the problem at the wrong level. OnBeforeUnload is simply not triggered because there is nothing being unloaded from the browsers perspective. Therefore you have, quite bluntly, implemented the wrong mechanism for versioning - fragments are for page states, not document states as you are using it now.
If you insist on maintaining state through hash fragments you need to use another mechanism to guard against page state changing. Since all current browsers support LocalStorage I'd use that. And well, while at it, put all the document state data there instead of in the URL, since that is how Google Docs does it and that is why they don't have this issue.

Is there a way to disable mouseover on highcharts entirely?

I'm building a website that uses highcharts. When I view the site on a mobile device, touching within the graph area pops up the tooltip, which prevents scrolling. I have tried all of the following, as suggested in other SO questions, without success:
$('#graph-container').click(function() { return false; });
$('#graph-container').children().click(function() { return false; });
chart.container.onclick = null;
plotOptions: {
series: {
enableMouseTracking: false // (stops tooltip but still blocks scrolling)
}
}
For now I've added a second div that covers the graph on mobile devices, so the user touches the div instead of the graph, but that is more a workaround than a solution. I also tried removing all listeners from every element of the graph using things like $('svg').off() in Chrome's console, without any noticeable change in the graph's behaviour. Is there a way to do this that I'm missing?
Highcharts JS v2.3.5 (2012-12-19)
Little HACK:
edit Line: 9026: this.setDOMEvents();
into: // this.setDOMEvents();
or delete it.
I hope it helped!
Here you can find simple Gist for that.
Also, in upcoming Highcharts 3.0 touch events should be upgraded and fixed similar issues. See roadmap: http://www.highcharts.com/support/roadmap

Ajax TabContainer Rendering Problem

I'm using Ajax TabContainer with a number of tabs,and IE as browser.
On one of the tabs, which is created and added dinamically, I'm showing a number of charts.
Sometimes some of the charts are missing, and after changing tab or selecting the area by mouse, the chart apears.
This problem exists only in production environment, in other words, in development environment there is no problem.
Thanks very much in advance for any help.
I could fix the issue, even though in a tricky way.
I forced the tabContainer to redraw after the page is loaded:
setTimeout(function() {
var tabIndex = $find("<%=TabContainer1.ClientID%>").get_activeTabIndex() ;
$find("<%=TabContainer1.ClientID%>").set_activeTabIndex(tabIndex);}
, 4000)
It does nothing but activating the tab which is already active.
However, I'm wondering if there is any better resolution.

Resources