NVD3 Chart Suppress Rendering for Hidden Charts - javascript

I faced a similar problem to what was described HERE:
The solution that worked for me, was to implement the following code:
$(function () {
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
window.dispatchEvent(new Event('resize'));
});
});
I have a feeling, however, that ALL of the charts are being re-rendered, regardless of whether they are on the active tab (visible) or in the non-selected tabs (hidden). If for example I have 20 tab pages, it takes far longer to re-render than it does for 2 tab pages.
Does anyone know how to ensure ONLY the active chart gets resized / redrawn? Ie how can the resize / redraw event be suppressed if the chart is not visible?

What I did was to store all my charts in an array when they are first created. I know that 'chart1' is a child of 'tab1', 'chart2' is a child of 'tab2' etc... (by design), so I can determine the index in the array using some regex.
Once the index is known, we can refresh the chart object directly, accessed from the array by zero-based index.
//Resize Event needs to be triggered when tab changes.
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
var plotID, ev;
try{
plotID = $(e.target).attr("href").replace(/[#A-Za-z$-]/g,"")
d3.select("#chart"+ plotID +" svg").call(charts[(plotID-1)])
}catch(err){ //Fallback
ev = document.createEvent('Event');
ev.initEvent('resize', true, true);
window.dispatchEvent(ev);
}
});
Net result, redraw times much much faster when compared to triggering resize event.

Related

Prevent child event from firing

I have a slider that I am currently making. I am making slow progress, but I am making progress nonetheless!
Currently I have this:
http://codepen.io/r3plica/pen/mEKyGG?editors=1011#0
There are 2 things you can do with this control, the first thing is you can drag left or right. The second thing you can do is click a "point" and it will scroll to the center.
The problem I have is that if I start dragging from a point, when I let go it will invoke the moveToCenter method.
I have tried to prevent this by adding
// Stop from accessing any child events
e.preventDefault();
e.stopPropagation();
to the end of the dragEventHandler, but this did not work.
I also have 2 boolean values options.drag and options.start. I though I might be able to use them somehow (if the drag has started and is enabled then don't perform the moveToCenter but this didn't work either.
Do anyone have any idea how to get this to work?
Maybe this will help. You can register your events in bubbling or capturing mode, using addEventListener method. It defines orders of processing your events - child -> parent (bubbling), or vice versa (capturing).
http://www.quirksmode.org/js/events_advanced.html
So, if you use addEventListener(event, handler, true), it will use capturing event mode.
Codepen:
http://codepen.io/anon/pen/bZKdqV?editors=1011
divs.forEach(function (div) {
div.addEventListener('click', function(e) {
e.stopPropagation();
console.log('parent');
}, true);
});
Be aware of browser support (IE9+). All modern browsers - yes, of course.
http://caniuse.com/#search=addeventlistener
Update
So it turned out to be easier than first approach. (no need for capturing)
Check out codepen:
http://codepen.io/anon/pen/QExjzV?editors=1010
Changes from your sample:
At the beginning of moveToCenter: function(e, options, animate) function
if (options.started) {
return;
}
In if (['mouseup', 'mouseleave'].indexOf(e.type) > -1):
setTimeout(function() {
options.started = false;
} , 100);
instead of
options.started = false;
Hope this helps.

jVectorMap generates a 100x100 when display object is initially hidden

So I'm having a problem generating my jVectorMap.
The map itself sits inside a very custom drop down menu that I have created and this is where I suspect the problem is.
When I mouseover my menu item to open up the drop down which contains the map the actual svg starts out with a forced dimension of 100px x 100px.
What I have tried to do a number of workarounds wher I call the "map.setSize()" either on the mouseclick event of the dropdown as well as the mouseover event of the container itself. The problem here is my dropdown is not subject to a click event but shows on the mouseover event. However, at the point of the mouseover event the actual container for the map hasn't loaded so I'm still stuck with a 100px x 100px svg.
To get around this I've put an event on the mouseover event of the container itself but this isn't great either as it then requires the user to move his mouse over the container before it actually shows the map, something I don't want to happen.
Is there a way of getting the map built inside a div which is invisible before my menu event occurs?
For an example of my problem I've created this at jsfiddle http://jsfiddle.net/AEup9/
You will notice that when you hover over the "Show Map" menu item (the only item) the drop down is blank except for the topic headers until you move the mouse over the actual drop down itself which then reloads the map. I then keep the map there by using my "loaded" variable created before my mouseover event and a force map.setSize() inside the same event:
var loaded = false;
$('#aamap').mouseover(function () {
if (!loaded) {
(function () {
map = new jvm.WorldMap({
map: 'za_mill_en',
container: $('#southafrica-map'),
backgroundColor: '#cbd9f5',
initial: {
fill: 'white'
},
series: {
regions: [{
attribute: 'stroke'
}]
}
});
loaded = true;
})();
}
map.setSize();
});
This is my rough work around but not what I really want as I want the map to show up first time.
Can anyone help me here?
Edit: I finally decided to NOT go ahead with using jvectormap due to this issue. Instead I opted to use jqvmap which is to some degree a fork of jvectormap, however the issues experienced with jvectormap were no longer a problem.
I met this issue as well.
To solve that problem we need to run an updateSize method on a map object when container of our map becomes visible. First, to get the map object we need to use this command:
$('#world-map').vectorMap('get', 'mapObject') and when execute updateSize on it, like:
var map = $('#world-map').vectorMap('get', 'mapObject');
map.updateSize();
or in a shorter form:
$('#world-map').vectorMap('get', 'mapObject').updateSize();

Jquery on mousedown not working on dynamically generated elements

So i'm trying to create a js/css "wave game" like tower defense ones.
When all the pre-generated enemys from first wave are dead, it spawns the second wave and so on.
So far so good.
The problem is that i just can't attack mobs dynamically spawned within second wave.
I used to try .live() in similar cases, but its deprecated, so i'm trying .on(), as instructed
$('.enemy').on('mousedown' , function(event) {
//attack code
}
Its working fine for initial mobs (1st wave) but it still just not working on dynamic mobs (>= 2nd wave)
Help, guys, please?
You need to specify an element that is already there when the DOM is created. In the parameters, you specify the elements you want to add the mousedown method. By simply assigning $('.enemy'), it will attach the method to those that are already present in the DOM.
$('body').on('mousedown', '.enemy', function(event) {
//attack code
}
As Wex mentioned in the comments, instead of writting $('body') you should use the container's name (the container which wraps the .enemy elements. This way, when a .enemy element is added, the event doesn't need to bubble all the way up to the body tag.
The binding '.on()' works only with the content that created earlier then the script ran.
So one solution could be you bind the event to the parent element.
$('.PARENT_ELEMENT').on('mousedown', '.enemy', function(event){
// your code here
}
That should do it.
I made this google like drop down suggestions search box and I faced a problem similar to yours where there was suggestions disappearing before the re-direct happened. I overcame it by using and modifing vyx.ca answer:
var mousedownHappened = false;
var clicked_link;
$("#search-box").blur(function(e) {
if (mousedownHappened)// cancel the blur event
{
mousedownHappened = false;
window.location.href = clicked_link;
} else {
// no link was clicked just remove the suggestions box if exists
if ($('#search-btn').next().hasClass('suggestions')) {
$(".suggestions").remove();
}
}
});
//attaching the event to the document is better
$(document).on('mousedown', '.suggestions a', function() {
clicked_link= $(this).attr('href');
mousedownHappened = true;
});

How can I add a `loading...` icon to jQuery UI's autocomplete widget

I'm trying to modify jQuery's autocomplete widget to have the input's background color change while the script is searching for suggestions. Here is my attempt (or have a look at my attempt on jsfiddle):
html
<label for="numbers">Numbers: </label>
<input id="numbers" />
javascript
var numbers = [],
nb = 10000,
i;
for (i = 0; i < nb; i += 1) {
numbers.push(i.toString());
}
function color (color) {
$('#numbers').css({
background: color
});
}
$("#numbers").autocomplete({
source: numbers,
search: function (event, ui) {
console.log('search event fired');
color("red");
},
open: function (event, ui) {
console.log('open event fired');
color("green");
},
close: function (event, ui) {
console.log('close event fired');
color("white");
}
});
As you can see, I'm logging the search, open and close events so I can see when they are fired.
As expected, typing in 5 (an existing value) fires the search event and logs the corresponding message, and, a few seconds later, fires the open event and logs its corresponding message. Since I put 10000 entries in there, there is a noticeable delay between the search event and the open event (something like 1 second).
What baffles me is that when the log message associated to the search event appears, it is not followed by the background color changing to red as should be expected. Instead, it remains white until the open event occurs, then becomes green (as expected after the open event). If, however, I type in a (a non-existing value), the background color goes red without problem (notice the open event never occurs in this case because no corresponding value is found). What's happening here?
For the curious, I'm ultimately trying to have a little loading icon appear while the search is underway (a feature which I am surprised isn't shipped with the widget out of the box). The background color change is a first step in that direction.
UPDATE
I made another attempt which shows that the instruction following console.log is indeed called, but the visual effects appear only much later. Here is the code I'm using:
$("#numbers").autocomplete({
source: numbers,
search: function (event, ui) {
console.log('search event fired');
$('#numbers').css({
marginTop: "5em"
});
console.log('search callback done');
}
});
If you try it out, you'll see that both messages are logged before the field is displaced by the addition of a top margin. Somehow, the margin is added at the right time, but the page is not being redrawn at the right time...
So, have you tried this?
$("#numbers").autocomplete({
source: numbers,
search: function (event, ui) {
console.log('search event fired');
$(this).addClass('working');
},
open: function (event, ui) {
console.log('open event fired');
color("green");
},
close: function (event, ui) {
console.log('close event fired');
color("white");
}
});
That should add a class to your textbox. Just add a CSS style called "working" with background property of red.
Ref. jQuery autocomplete: How to show an animated gif loading image
The short answer: it's not your fault :) What's happening, is that the painting of the browsers is basically stalled because of the autocomplete plugin being extemely slow.
To identify how slow and why it's slow, you can use Chrome Devtool's timeline tab. While in frame mode, press record to see a graph like this:
You'll see the yellow bars (Scripts) go way above the bar's outlines once you start entering input. This means the operations cannot be completed in time thus causing the browser to stall. You can view the stacktrace to determine what lines exactly are really slow. The problem is fixable though, but it will require quite some knowledge of the jquery-ui internals.
When working with this many options, frontend scripting may not suffice. You could look into a server-side search with ajax frontend so the heavy lifting is done by the server.

js/jQuery Drag'n'Drop, recalculate the drop targets

I have the following issue, I have a large tree which has subnodes which can be folded and unfolded on demand (the data within nodes gets fetched with AJAX). However, I use jquery.event.drop/drag to create my drag/drop targets.
However, when I fold/unfold the drop targets change position and I need to recalculate. This is how I wanted to do that:
function create_drop_targets() {
$('li a')
.bind('dropstart', function(event) {
})
.bind('drop', function(event) {
})
.bind('dropend', function(event) {
});
}
create_drop_targets() is called upon fold/unfold.
However, this doesn't work. I have located the following within jquery.event.drop:
var drop = $.event.special.drop = {
setup: function(){
drop.$elements = drop.$elements.add( this );
drop.data[ drop.data.length ] = drop.locate( this );
},
locate: function( elem ){ // return { L:left, R:right, T:top, B:bottom, H:height, W:width }
var $el = $(elem), pos = $el.offset(), h = $el.outerHeight(), w = $el.outerWidth();
return { elem: elem, L: pos.left, R: pos.left+w, T: pos.top, B: pos.top+h, W: w, H: h };
}
Now I need to know how I can call the setup() method again so it repopulates $elements with the new positions for the droppables.
Just had the same issue. I wandered around within the source-code of jQuery and found this (in ui.droppable.js):
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
...
So, you'd just have to use
$(".cocktails").draggable({
refreshPositions: true,
});
Seems not to be documented very much... but it fixed my problem. Makes everything a bit slower of course, I would advise some usage-dependent tweaking (enable it before the changes occur, and disable it once the user has moved his mouse and the changes have occured).
Maybe it will be better to add live events introduced in jQuery 1.3?
$("li a").live("dropstart", function(){...});
I ran into the same issue when I tried to combine scrolling with draggable rows in liteGrid, but I found a work-around. Your mileage may vary, but what I did was add logic to my drag event handler that would check to see if the grid was being scrolled (which is when I needed to force the droppable positions to be refreshed), and if so, I set refreshPositions to true on the draggable. This doesn't immediately refresh the positions, but it will cause them to refresh the next time the drag handle moves. Since refreshPositions slows things down, I then re-disable it the next time my drag event handler fires. The net result is that refreshPositions is enabled only when the grid is scrolling in liteGrid, and its disabled the rest of the time. Here's some code to illustrate:
//This will be called every time the user moves the draggable helper.
function onDrag(event, ui) {
//We need to re-aquire the drag handle; we don't
//hardcode it to a selector, so this event can be
//used by multiple draggables.
var dragHandle = $(event.target);
//If refreshOptions *was* true, jQueryUI has already refreshed the droppables,
//so we can now switch this option back off.
if (dragHandle.draggable('option', 'refreshPositions')) {
dragHandle.draggable('option', 'refreshPositions', false)
}
//Your other drag handling code
if (/* logic to determine if your droppables need to be refreshed */) {
dragHandle.draggable('option', 'refreshPositions', true);
}
}
$("#mydraggable").draggable({
//Your options here, note that refreshPositions is off.
drag: onDrag
});
I hope that saves you from ramming your head into the keyboard as many times as I did...
I realize the original question is quite old now, but one little trick I came up with to refresh the position of draggable elements without much overhead (AFAICT) is to disable and immediately re-enable them wherever appropriate.
For instance, I noticed that resizing my browser window would not refresh the position of my draggable table rows, so I did this:
$(window).resize(function () {
$(".draggable").draggable("option", "disabled", true);
$(".draggable").draggable("option", "disabled", false);
});
I hope this helps someone out there!

Categories

Resources