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.
Related
I'm using Leaflet for a mobile app developed with Ionic. I currently have a function to search for certain items near a location specified when you make a single click on the map
$rootScope.map.on('click', function(e) {
if (APP_STATUS == ACTION_SEARCH) {
positionClick = e.latlng;
$scope.positionSearchClose = positionClick;
$scope.userPosition.setLatLng(positionClick);
$scope.popupSearchRoutesClosed = $ionicPopup.show({
template: $scope.getTemplate(),
title: 'Buscando rutas',
scope: $scope,
});
$scope.getRoutesClose(positionClick, ACTION_SEARCH);
}
});
$scope.addButtons();
I want to change this so that the search happens when the user holds the touch for a brief time. I changed the 'click' parameter to 'contextmenu' and I achieved what I was looking for, but I found out that the hold timing was too long. Despite the leaflet doc description of the ContextMenu event being "Also fired on mobile when the user holds a single touch for a second (also called long press)." it feels like that "second" is an eternity.
Is there a way to make it so the function above only triggers when the user holds the touch, but specifying the amount of time of the hold?
it maybe long time. But i've been searching for this for few hours. i found the solution, which may help anyone who comes here.
In leaflet, touch tap delay declared as 1000ms in a settimeout function. And they can be edited.
In leaflet.js find the below code :
// simulate long hold but setting a timeout
this._holdTimeout = setTimeout(bind(function () {
if (this._isTapValid()) {
this._fireClick = false;
this._onUp();
this._simulateEvent('contextmenu', first);
}
}, this), 1000);
now the 1000(1 second) declared here is the timer, you can change the 1000 to whatever you like...like 300 or 500.
if you want to change touch hold(tap) timer dynamically,
create a variable in the leaflet.js beginning
var contextMenuTime = 1000;
and manually apply the variable in js the function
...
this._simulateEvent('contextmenu', first);
}
}, this), contextMenuTime);
now you can control the tap delay by changing the variable value.
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.
I'm using dragswipe, a plugin from here. It works perfectly fine. However, I have a requirement to do dynamic carousels. So, every time a user changes something on a page, the carousels get updated dynamically. I thought I could just recall the plugin to update the element, but somehow when I dragged the carousel the functional gets called multiple times.
For example, I do this to initialise the plugin.
function init() {
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
}
So after the page is updated via ajax I have a callback to call this method again like this
function callback() {
init();
}
Everything should is updated perfectly fine, but when I started dragging. The carousel skips some pages. I thought I had to unbind all the events so I tried this. $('#carousel').unbind() but the problem still persists.
Here's the source code when the plugin is initialised.
$.fn.dragswipe = function(options) {
options = $.extend({
offset: 0,
turn_threshold: 0.1,
current_page: 0
},options)
this.each(function() {
option = $(this).hammer({
drag_vertical: false,
swipe_time: 20
});
// start a drag.
// if we're moving left or right, set our initial distance to 0.
$(this).bind('dragstart',function(ev) {
console.log('dragstart');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = 0;
}
});
// while dragging, change the dragging distance based on how far a user has dragged.
$(this).bind('drag',function(ev) {
console.log('drag');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = ev.distanceX;
var options = CONFIGS[$(this).data('dragswipe_id')];
$(this).updateOffset(options.offset+drag_distance);
}
});
$(this).bind('dragend',function(ev) {
console.log('dragend');
if (ev.direction=='left' || ev.direction=='right') {
var options = CONFIGS[$(this).data('dragswipe_id')];
if (Math.abs(drag_distance / options.width) > options.turn_threshold) {
if (ev.direction=='left') {
options.current_page++;
}
if (ev.direction=='right') {
options.current_page--;
}
}
// store modified options
CONFIGS[$(this).data('dragswipe_id')] = options;
console.log(options.current_page);
$(this).gotoPage(options.current_page,true);
}
});
// set the dragswipe ID used to look up config options later.
$(this).data('dragswipe_id',CONFIGS.length);
// store config options.
CONFIGS[$(this).data('dragswipe_id')] = options;
});
}
Which I see nothing wrong with the plugin, but maybe I'm missing something obvious.
UPDATED
I have created the example in jsfiddle, but it's not working just in case anyone can fix the problem. Also, on the plugin site itself. The problem can be reproduce by running this code to initialise the plugin multiple times. After running the code twice, when you drag the page it goes to the last page instead of the second page.
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
Have you tried turning it off and on again?
Dragswipe has a removeDragswipe() method that does a little bit more than just unbinding, and it also explicitly only unbinds events that are related to dragswipe itself so it won't mess up any other events that may have been bound.
So instead of unbind(), do this:
function callback() {
$('#carousel').removeDragswipe();
init();
}
As discussed in the comments, this doesn't work because it seems that even after unbinding all the events, after re-binding them by activating the plugin again the events seem to fire an extra time.
What does seem to be a workaround is to actually rebuild the entire element so that no events can linger:
function callback() {
$('#carousel').removeDragswipe();
rebuildCarousel();
init();
}
function rebuildCarousel() {
var wrapper = $('#carousel').parent;
var html = parent.html();
$('#carousel').remove();
wrapper.html(html);
}
I am trying too keep two instances of an Ace editor in sync. So when the user types in one, the other is updated.
Looking at their docs I see that the EditSession change event says that it returns a delta of the change, and the Document has an applyDeltas method.
So I have hooked into that change event, and when it is fired I call the other document.applyDeltas and pass it over, but it doesn't work.
I have been poking around their docs (and Google for an hour), but I am not seeing how to keep them in sync. Does anyone know how I can do that?
Ok, I figured it out. Nothing beats looking at src :)
The applyDeltas method on the document wants an array, AND you need to grab the data from the change event.
//on editor1.change
this.handleEditor1Changed = function (e) {
var deltas = new Array();
deltas[0] = e.data;
this.editor2.getSession().getDocument().applyDeltas(deltas);
};
If you looked at the source, you should have seen that applyDeltas just calls applyDelta in a loop for each element in the array. Thus, you could simply do this:
editor1.on('change', function(delta) {
editor2.session.doc.applyDelta(delta);
})
or, in more modern JavaScript
editor1.on('change', delta => editor2.session.doc.applyDelta(delta))
What I'm trying to do is create a class that I can quickly attach to links, that will fetch and display a thumbnail preview of the document being linked to. Now, I am focusing on ease of use and portability here, I want to simply add a mouseover event to links like this:
Testing
I realize there are other ways I can go about this that would solve my issue here, and I may end up having to do that, but right now my goal is to implement this as above. I don't want to manually add a mouseout event to each link, and I don't want code anywhere other than within the class (and the mouseover event creating the class instance).
The code:
TestClass = new Class({
initialize: function(anchor) {
this.anchor = $(anchor);
if(!this.anchor) return;
if(!window.zzz) window.zzz = 0;
this.id = ++window.zzz;
this.anchor.addEvent('mouseout', function() {
// i need to get a reference to this function
this.hide();
}.bind(this));
this.show();
},
show: function() {
// TODO: cool web 2.0 stuff here!
},
hide: function() {
alert(this.id);
//this.removeEvent('mouseout', ?); // need reference to the function to remove
/*** this works, but what if there are unrelated mouseout events? and the class instance still exists! ***/
//this.anchor.removeEvents('mouseout');
//delete(this); // does not work !
//this = null; // invalid assignment!
//this = undefined; // invalid assignment!
}
});
What currently happens with the above code:
1st time out: alerts 1
2nd time out: alerts 1, 2
3rd time out: alerts 1, 2, 3
etc
Desired behavior:
1st time out: alerts 1
2nd time out: alerts 2
3rd time out: alerts 3
etc
The problem is, each time I mouse over the link, I'm creating a new class instance and appending a new mouseout event for that instance. The class instance also remains in memory indefinitely.
On mouseout I need to remove the mouseout event and destroy the class instance, so on subsequent mouseovers we are starting fresh.
I could create a helper function for this to make sure that the class is only created once for each link, something like this:
function TestClassHelper(anchor) {
anchor = $(anchor);
if(!anchor) return;
if(!anchor.retrieve('TestClass')) anchor.store('TestClass', new TestClass(anchor));
anchor.retrieve('TestClass').show();
}
Testing
I may end up implementing it this way if I have to, but I'm curious as to how I can fix the other method.
This looks a lot more complex than it should be. But if you want to fix this, you need to save a reference to the bound function somewhere and later pass that to removeEvent.
For example:
// inside initialize
this.boundHandler = function() {
this.hide();
}.bind(this)
this.anchor.addEvent('mouseout', this.boundHandler);
// inside hide
this.removeEvent('mouseout', this.boundHandler);
See the removeEvent docs for examples of this very issue.
I wouldn't recommend event delegation here either for performance reasons. The best approach is to attach the handler in code rather than inline and only do it once, so unnecessary objects are not being created each time the user mouses over.