fullcalendar event rendering performance issue - javascript

So, basically all my events(there's min. 360 of them) have team1 vs. team2 or - vs. team2 or team1 vs. - placeholders.
And on the initial render events change color depending on whether the event has one or two teams.
Orange color for the one team , and green for the two teams. Also, the event changes color on click.
But mostly, I'm interested in increasing performance with rendering events.
Rendering performance is going really bad in fullCalendar, and I couldn't find any solution to this problem.
So here's my code:
eventRender: function (event, element) {
$(element).append((event.teams[0] != null ? event.teams[0] : '-') + '</br> vs. </br>' + (event.teams[1] != null ? event.teams[1] : '-'));
if (event.teams.length === 1) {
$(element).css('background', 'orange');
}
else if (event.teams.length > 1) {
$(element).css('background', 'green');
}
}
My main issue is that when I click on event to change its color, the script automatically goes to the eventRender or eventAfterRender event, and its behavior is exactly like the for statement - it iterates over events and then it does the stuff that I want to do with the individual event, but only when the loop lands on the clicked event.
Also, in the eventClick I've called $('#myCalendar').fullcalendar('updateEvent',event) and I think there is a bug, because it automatically goes to the eventAfterRender or the eventRender, iterating over the whole events collection again.
Even tough 'updateEvent' parameter should instruct fullCalendar to update/render only the specific event.
Does anyone have any advice on this subject?

Fullcalendar now supports the renderEvents method: https://fullcalendar.io/docs/renderEvents.
Simply build your events list and send them all at once:
$("#calendar").fullCalendar('renderEvents', events, true);

I know this is an old question, but i solved the same performance problem in v5 of the fullcalendar with this configuration option:
https://fullcalendar.io/docs/rerenderDelay
It basically adds a delay after each operation that would trigger a render event.
if the framework detects another operation within that delay, it renders these events in one operation and thereby increases performance.
setting the value to 1 (so 1 millisecond delay) did the trick for me. I simply added it to the configuration in my angular component:
calendarOptions: CalendarOptions = {
...,
rerenderDelay: 1,
}

In fullcalendars source-code (at least in my version of it) there is the renderEvent-handler, that calls reportEvents -function which is the bottleneck of performance. I worked my way around this issue, by adding handling of mass-rendering events to the source-code.
I wrote a short function:
function massRenderEvents(events, stick) {
var i;
for (i = 0; i < events.length; i += 1) {
normalizeEvent(events[i]);
if (!events[i].source) {
if (stick) {
stickySource.events.push(events[i]);
events[i].source = stickySource;
}
cache.push(events[i]);
}
}
reportEvents(cache);
}
Under "EventManager" -function, and added it to EventManagers exports, like:
t.massRenderEvents = massRenderEvents;
Now, for every batch of rendered events, the heavy and slow reportEvents is called just once. Note, that massRenderEvents -function is very similar to the original renderEvent -function.

I have changed
$("#calendar").fullCalendar('renderEvent', eventData1, true);
to
$("#calendar").fullCalendar('addEventSource', eventData1, true);
and that worked for me. I have read the issue on several related website and as per their suggestion I have done this.
The main difference between renderEvent and addEventSource is that the first one tries to interact with calendar when even a single event created which take much time because of regular callback function, and the second one sends a bucket of JSON events to calendar which require only single callback function which improve the performance and take less time.

Related

Possible to simulate select using setSelection?

I have a project where I am using the vis.js timeline module as a type of image carousel where I have a start and an end time, plot the events on the timeline, and cycle through them automatically and show the image attached to each event in another container.
I already have this working and use something similar to the following to accomplish this, except one part:
var container = document.getElementById('visualization');
var data = [1,2,3,4,5];
var timeline = new vis.Timeline(container, data);
timeline.on('select', function (properties) {
// do some cool stuff
}
var i = 0;
(function timelapseEvents(i) {
setTimeout(function(){
timeline.setSelection(data[i], {focus: true, animation:true});
if (i < data.length - 1) {
timelapseEvents(i+1);
}
}, 2000);
})(i)
The timeline.setSelection() part above works, the timeline event is selected and focused on. However, the "select" event is NOT triggered. This is verified as working as expected in the documentation (under Events > timeline.select) where it says: Not fired when the method timeline.setSelection() is executed.
So my question is, does anyone know how to use the timeline.setSelection() method and actually trigger the select event? Seems unintuitive to me to invoke the timeline.setSelection()method and not actually trigger the select event.
Spent a few hours on this and came up short. I ended up just taking the code I had in my timeline.on('select', function (properties) { block and turning it into a function and calling it after the timeline.setSelection() call.
Basically, I didn't fix the issue but worked around it. Will keep an eye on this in case anyone actually is able to figure out how to add the select() event to the setSelection() method.

Execute Event after NativeScript Slider observable stops changing

I have a slider with the value bound to an observable, all works great there.
I've setup the Observable.propertyChangeEvent on the slider so I'm getting the event when the observable is changed (when the user drags the slider).
slider.on(Observable.propertyChangeEvent, function(data: PropertyChangeData) {
console.log('EventName: ' + data.eventName);
console.log('PropName: ' + data.propertyName);
console.log('Value: ' + data.value);
})
What I want:
I'd like to execute an event once the observable quits changing for a set period of time (example: 1000ms). To top it off, a way to stop this event if the observable starts changing again. I need to do some processing once the user sets the slider at its destination to update some other parts of the UI. So if the user starts changing it again, for performance purposes it might be best to STOP that processing and then execute once the changes are done by the user adjusting the slider.
I'm thinking set a variable and then start a timer but it's not really clicking right now for me :)
Update with Answer suggestion using Underscore.js _.debounce()
let underscore = require("underscore");
function debouncedValue(data: PropertyChangeData) {
console.log('NewSlider value: ' + data.value);
}
let debouncedSlider = underscore.debounce(debouncedValue, 800);
slider.on(Observable.propertyChangeEvent, debouncedSlider);
In UnderscoreJS you have a function called debounce that does exactly what you are trying to achieve here.
From the docs:
_.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function which will postpone its
execution until after wait milliseconds have elapsed since the last
time it was invoked. Useful for implementing behavior that should only
happen after the input has stopped arriving. For example: rendering a
preview of a Markdown comment, recalculating a layout after the window
has stopped being resized, and so on.

Jquery or Javascript Animating the Z-Index

I need the functionality of animating the z-index property of a specific HTML object. I've been able to achieve this animation in two ways that both have their difficulties/drawbacks. Successfully answering this question for me will fix one of the following two issues:
The first is by adapting the JQuery animate command with the step functionality outlined here by the accepted answer:
jQuery's $('#divOne').animate({zIndex: -1000}, 2000) does not work?
The problem with this method for me is that the $('#obj').stop(); command cannot prematurely end the animation when done in this way. It always finishes unless I destroy the object I'm working with and create a new one (which causes blinking obviously). If anyone knows of a way to properly stop a step animation like this, or a work-around for the issue, I'd love to see it.
var div = $('#obj');
$({z: ~~div.css('zIndex')}).animate({z: [-2000, 'linear']}, {
step: function() {
div.css('zIndex', ~~this.z);
},
duration: 10000
});
The second is using a setInterval loop on 20 MS (a speed that is sufficient for my needs) to simply adjust the z-index to what it should be at that point of the "animation". This works great for a few moments, then something causes it to stop working suddenly. The code still runs through the $('#obj').css('z-index', val); line, and val is changing, but it no longer updates the object in the DOM. I've tried it on slower timer settings as well with identical results. Anyone know why JQuery might suddenly no longer be able to set the Z-Index?
function () move {
if (!(MoveX == 0 && MoveY == 0))
{
$('#obj').css('z-index', val);
}
}
$('#obj').stop() doesn't work for you because the animation isn't being performed on $('#obj').
It is being performed on the object $({z: ...}) (with a custom step function). This means you should do something like
var anim = $({z: ~~div.css('zIndex')}).animate({z: [-2000, 'linear']}, {
step: function() {
div.css('zIndex', ~~this.z);
},
duration: 10000
});
// sometime later
anim.stop();
See this demo.
Edit For what it's worth, here is the same demo using an animation interval. I see a syntax error in your second snippet: the function declaration should be
function move() { ...
I assume that's a typo since your code wouldn't even parse. Other than that, I'm not sure why that solution didn't work for you.

Implementing location.hash bound bidirectionally to AJAX record-context

I have a simple javascript AJAX application that allows search and selection of records. Selection updates the location.hash and loads the associated record detail, and ideally vice-versa also (loading a record when the hash changes). Of course a careless implementation can cause loops and extra panel flashes.
I want a predictable and concise implementation of this bidirectional binding.
One approach is to only load a record on the hashchange event, and when a record is selected in the UI, set location.hash. This seems most concise, but I'd be concerned this would diminish record-click responsiveness in older browsers with a polled hashchange shim.
Another approach is to record a navigating (e.g.) state when selecting a record, and clear it when handling hashchange. That's covered in this question. However, that seems like certain event sequences, like tapping Back multiple times rapidly, might result in inconsistency between the displayed content and URL.
Have you seen an implementation that solves these problems?
Why not to use HTML5 history API instead? All modern browsers support it
To make things easier you can use history.js library to work with History/State APIs
Using that library you can subscribe to URL updates
History.Adapter.bind(window, 'statechange', function () {
// event handler code here
var state = History.getState();
}
or push new URL into history
History.pushState(null, "name", "http://newurl");
I'm not sure which JS framework you would like to use to get bidirectional binding, but with KnockoutJS you can create ko.computed object with read and write methods
I think there's a simple answer in my case, since it's a read-only/idempotent operation (well, it actually logs a view).
I'll just store the state displayed by the current content, and test it on each event that would load content (including the 'redundant' hashchange events), ignoring the event if it matches the currently-displayed state.
Seems cheap, for better or worse. :)
here's my approximate/pseudo-code:
var activeRecordId;
function loadHash() {
var idList = window.location.hash.substring(1).split(',');
if (idList.length > 1) loadSpecificRecordsToList(idList);
else if (idList != '') loadDetailRecord(idList[0]);
}
function loadDetailRecord(id) {
if (id != activeRecordId) {
activeRecordId = id;
doDetailLoadAjaxAndSuch(id);
}
}
$(function () {
loadHash();
$.bind('hashchange', loadHash);
});

An efficient way to cancel a mouseover event... read on to see why

I'm in the process of authoring a completely client side web language reference site. A problem that I encountered today; I have a side panel that is a unordered list of terms and they have onmouseover event listeners. I decided it would be a good idea to add a delay prior to execution and cancel the event at run-time if the mouse was no longer over that element. This is what I've come up with but I feel there must be a better way.
var currentXCoordinate=0
var currentYCoordinate=0
var elementFromCurrentMousePosition=0
function trackCurrentMousePosition(event) {
if (document.elementFromPoint(event.clientX, event.clientY).nodeName=="SPAN") {
elementFromCurrentMousePosition=document.elementFromPoint(event.clientX, event.clientY).parentNode
}
else {
elementFromCurrentMousePosition=document.elementFromPoint(event.clientX, event.clientY)
}
return (currentXCoordinate=event.clientX, currentYCoordinate=event.clientY, elementFromCurrentMousePosition)
}
function initPreview(event, obj) {
arg1=event
arg2=obj
setTimeout("setPreviewDataFields(arg1, arg2)", 100)
}
function setPreviewDataFields(event, obj) {
if ('bubbles' in event) {
event.stopPropagation()
}
else {
event.cancelBubble=true
}
if (elementFromCurrentMousePosition!=obj) {
return 0;
}
The code goes on to do all the wonderful stuff I want it to do if execution wasn't cancelled by the previous if statement. The problem is this method is seeming to be really processor intensive.
To sum it up: on page load all my event listeners are registered, cursor position is being tracked by a onmousemove event. Applicable list items have a onmouseover event that calls the initPreview function which just waits a given period of time before calling the actual setPreviewDataFields function. If at run-time the cursor is no longer over the list element the function stops by return 0.
Sadly that's the best I could come up with. If anyone can offer up a better solution I would be very grateful.
Why not just use mouseout to tell when the mouse leaves an element? Running all of that code every time the mouse moves isn't ideal.
Also, you really shouldn't pass a string to setTimeout like that. Instead, pass a function. As a bonus, you can get rid of those evil global variables arg1 and arg2. With those being globals, I think you will run into issues if init gets called again before the timeout expires.

Categories

Resources