event binding optimization, fixed optimization-related mouseover bug

This commit is contained in:
Adam Shaw 2010-02-02 10:50:30 -08:00
parent 0e3e04bbdf
commit 16edfe71e7
3 changed files with 91 additions and 75 deletions

View file

@ -347,7 +347,7 @@ function Agenda(element, options, methods) {
/* Event Rendering /* Event Rendering
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
function renderEvents(events) { function renderEvents(events, modifiedEventId) {
view.reportEvents(cachedEvents = events); view.reportEvents(cachedEvents = events);
var i, len=events.length, var i, len=events.length,
dayEvents=[], dayEvents=[],
@ -359,14 +359,14 @@ function Agenda(element, options, methods) {
slotEvents.push(events[i]); slotEvents.push(events[i]);
} }
} }
renderDaySegs(compileDaySegs(dayEvents)); renderDaySegs(compileDaySegs(dayEvents), modifiedEventId);
renderSlotSegs(compileSlotSegs(slotEvents)); renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
} }
function rerenderEvents() { function rerenderEvents(modifiedEventId) {
clearEvents(); clearEvents();
renderEvents(cachedEvents); renderEvents(cachedEvents, modifiedEventId);
} }
@ -427,7 +427,7 @@ function Agenda(element, options, methods) {
// renders 'all-day' events at the top // renders 'all-day' events at the top
function renderDaySegs(segs) { function renderDaySegs(segs, modifiedEventId) {
if (options.allDaySlot) { if (options.allDaySlot) {
_renderDaySegs( _renderDaySegs(
segs, segs,
@ -445,7 +445,8 @@ function Agenda(element, options, methods) {
return axisWidth + colContentPositions.right(day2col(dayOfWeek)); return axisWidth + colContentPositions.right(day2col(dayOfWeek));
}, },
daySegmentContainer, daySegmentContainer,
bootstrapDayEventHandlers bindDaySegHandlers,
modifiedEventId
); );
updateSize(viewWidth, viewHeight); // might have pushed the body down, so resize updateSize(viewWidth, viewHeight); // might have pushed the body down, so resize
} }
@ -455,7 +456,7 @@ function Agenda(element, options, methods) {
// renders events in the 'time slots' at the bottom // renders events in the 'time slots' at the bottom
function renderSlotSegs(segs) { function renderSlotSegs(segs, modifiedEventId) {
var i, segCnt=segs.length, seg, var i, segCnt=segs.length, seg,
event, event,
@ -467,7 +468,7 @@ function Agenda(element, options, methods) {
outerWidth, outerWidth,
left, left,
html='', html='',
_eventElements, eventElements,
eventElement, eventElement,
triggerRes, triggerRes,
vsideCache={}, vsideCache={},
@ -527,13 +528,13 @@ function Agenda(element, options, methods) {
"</div>"; "</div>";
} }
slotSegmentContainer[0].innerHTML = html; slotSegmentContainer[0].innerHTML = html;
_eventElements = $.makeArray(slotSegmentContainer[0].childNodes); // TODO: look at .children() again eventElements = slotSegmentContainer.children();
// retrieve elements, run through eventRender callback, bind event handlers // retrieve elements, run through eventRender callback, bind event handlers
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
event = seg.event; event = seg.event;
eventElement = $(_eventElements[i]); eventElement = $(eventElements[i]); // faster than eq()
triggerRes = view.trigger('eventRender', event, event, eventElement); triggerRes = view.trigger('eventRender', event, event, eventElement);
if (triggerRes === false) { if (triggerRes === false) {
eventElement.remove(); eventElement.remove();
@ -549,11 +550,17 @@ function Agenda(element, options, methods) {
.appendTo(slotSegmentContainer); .appendTo(slotSegmentContainer);
} }
seg.element = eventElement; seg.element = eventElement;
bootstrapSlotEventHandlers(event, seg, eventElement); if (event._id === modifiedEventId) {
bindSlotSegHandlers(event, eventElement, seg);
}else{
eventElement[0]._fci = i; // for lazySegBind
}
view.reportEventElement(event, eventElement); view.reportEventElement(event, eventElement);
} }
} }
lazySegBind(slotSegmentContainer, segs, bindSlotSegHandlers);
// record event sides and title positions // record event sides and title positions
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
@ -611,11 +618,7 @@ function Agenda(element, options, methods) {
function bootstrapDayEventHandlers(event, seg, eventElement) { function bindDaySegHandlers(event, eventElement, seg) {
function mouseover(ev) {
view.trigger('eventMouseover', this, event, ev);
eventElement.unbind('mouseover', mouseover);
setTimeout(function() { // because IE will immediately trigger eventElementHandlers's mouseover
view.eventElementHandlers(event, eventElement); view.eventElementHandlers(event, eventElement);
if (event.editable || event.editable == undefined && options.editable) { if (event.editable || event.editable == undefined && options.editable) {
draggableDayEvent(event, eventElement, seg.isStart); draggableDayEvent(event, eventElement, seg.isStart);
@ -623,18 +626,11 @@ function Agenda(element, options, methods) {
view.resizableDayEvent(event, eventElement, colWidth); view.resizableDayEvent(event, eventElement, colWidth);
} }
} }
},0);
}
eventElement.mouseover(mouseover);
} }
function bootstrapSlotEventHandlers(event, seg, eventElement) { function bindSlotSegHandlers(event, eventElement, seg) {
function mouseover(ev) {
view.trigger('eventMouseover', this, event, ev);
eventElement.unbind('mouseover', mouseover);
setTimeout(function() { // because IE will immediately trigger eventElementHandlers's mouseover
view.eventElementHandlers(event, eventElement); view.eventElementHandlers(event, eventElement);
if (event.editable || event.editable == undefined && options.editable) { if (event.editable || event.editable == undefined && options.editable) {
var timeElement = eventElement.find('span.fc-event-time'); var timeElement = eventElement.find('span.fc-event-time');
@ -643,9 +639,6 @@ function Agenda(element, options, methods) {
resizableSlotEvent(event, eventElement, timeElement); resizableSlotEvent(event, eventElement, timeElement);
} }
} }
},0);
}
eventElement.mouseover(mouseover);
} }

View file

@ -370,9 +370,9 @@ function Grid(element, options, methods) {
} }
function rerenderEvents() { function rerenderEvents(modifiedEventId) {
clearEvents(); clearEvents();
renderSegs(compileSegs(cachedEvents)); renderSegs(compileSegs(cachedEvents), modifiedEventId);
} }
@ -409,7 +409,7 @@ function Grid(element, options, methods) {
function renderSegs(segs) { function renderSegs(segs, modifiedEventId) {
_renderDaySegs( _renderDaySegs(
segs, segs,
rowCnt, rowCnt,
@ -420,7 +420,8 @@ function Grid(element, options, methods) {
dayContentPositions.left, dayContentPositions.left,
dayContentPositions.right, dayContentPositions.right,
segmentContainer, segmentContainer,
mouseoverBind bindSegHandlers,
modifiedEventId
); );
} }
@ -437,12 +438,7 @@ function Grid(element, options, methods) {
function bindSegHandlers(event, eventElement, seg) {
function mouseoverBind(event, seg, eventElement) {
function mouseover(ev) {
eventElement.unbind('mouseover', mouseover);
view.trigger('eventMouseover', this, event, ev);
setTimeout(function() { // because IE will immediately trigger the new mouseover handlers
view.eventElementHandlers(event, eventElement); view.eventElementHandlers(event, eventElement);
if (event.editable || event.editable == undefined && options.editable) { if (event.editable || event.editable == undefined && options.editable) {
draggableEvent(event, eventElement); draggableEvent(event, eventElement);
@ -450,9 +446,6 @@ function Grid(element, options, methods) {
view.resizableDayEvent(event, eventElement, colWidth); view.resizableDayEvent(event, eventElement, colWidth);
} }
} }
},0);
}
eventElement.mouseover(mouseover);
} }
@ -519,7 +512,7 @@ function Grid(element, options, methods) {
}; };
function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, mouseoverBind) { function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContentLeft, dayContentRight, segmentContainer, bindSegHandlers, modifiedEventId) {
var options=view.options, var options=view.options,
rtl=options.isRTL, rtl=options.isRTL,
@ -528,7 +521,7 @@ function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContent
className, className,
left, right, left, right,
html='', html='',
_eventElements, eventElements,
eventElement, eventElement,
triggerRes, triggerRes,
hsideCache={}, hsideCache={},
@ -572,17 +565,20 @@ function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContent
:'') + :'') +
"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" + "<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
"</a>" + "</a>" +
((event.editable || event.editable == undefined && options.editable) && !options.disableResizing && $.fn.resizable ?
"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
: '') +
"</div>"; "</div>";
seg.left = left; seg.left = left;
seg.outerWidth = right - left; seg.outerWidth = right - left;
} }
segmentContainer[0].innerHTML = html; segmentContainer[0].innerHTML = html;
_eventElements = $.makeArray(segmentContainer[0].childNodes); // TODO: look at .children() again eventElements = segmentContainer.children();
// retrieve elements, run through eventRender callback, bind handlers // retrieve elements, run through eventRender callback, bind handlers
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
eventElement = $(_eventElements[i]); eventElement = $(eventElements[i]); // faster than eq()
event = seg.event; event = seg.event;
triggerRes = view.trigger('eventRender', event, event, eventElement); triggerRes = view.trigger('eventRender', event, event, eventElement);
if (triggerRes === false) { if (triggerRes === false) {
@ -598,11 +594,17 @@ function _renderDaySegs(segs, rowCnt, view, minLeft, maxLeft, getRow, dayContent
.appendTo(segmentContainer); .appendTo(segmentContainer);
} }
seg.element = eventElement; seg.element = eventElement;
mouseoverBind(event, seg, eventElement); if (event._id === modifiedEventId) {
bindSegHandlers(event, eventElement, seg);
}else{
eventElement[0]._fci = i; // for lazySegBind
}
view.reportEventElement(event, eventElement); view.reportEventElement(event, eventElement);
} }
} }
lazySegBind(segmentContainer, segs, bindSegHandlers);
// record event horizontal sides // record event horizontal sides
for (i=0; i<segCnt; i++) { for (i=0; i<segCnt; i++) {
seg = segs[i]; seg = segs[i];
@ -671,3 +673,23 @@ function cssKey(_element) {
} }
function lazySegBind(container, segs, bindHandlers) {
container.unbind('mouseover').mouseover(function(ev) {
var parent=ev.target, e,
i, seg;
while (parent != this) {
e = parent;
parent = parent.parentNode;
}
if ((i = e._fci) != undefined) {
e._fci = undefined;
seg = segs[i];
bindHandlers(seg.event, seg.element, seg);
$(ev.target).trigger(ev);
}
ev.stopPropagation();
});
}

View file

@ -117,27 +117,29 @@ var viewMethods = {
eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) { eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
var view = this, var view = this,
oldAllDay = event.allDay; oldAllDay = event.allDay,
view.moveEvents(view.eventsByID[event._id], dayDelta, minuteDelta, allDay); eventId = event._id;
view.moveEvents(view.eventsByID[eventId], dayDelta, minuteDelta, allDay);
view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs
// TODO: investigate cases where this inverse technique might not work // TODO: investigate cases where this inverse technique might not work
view.moveEvents(view.eventsByID[event._id], -dayDelta, -minuteDelta, oldAllDay); view.moveEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
view.rerenderEvents(); view.rerenderEvents();
}, ev, ui); }, ev, ui);
view.eventsChanged = true; view.eventsChanged = true;
view.rerenderEvents(); view.rerenderEvents(eventId);
}, },
eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) { eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) {
var view = this; var view = this,
view.elongateEvents(view.eventsByID[event._id], dayDelta, minuteDelta); eventId = event._id;
view.elongateEvents(view.eventsByID[eventId], dayDelta, minuteDelta);
view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() { view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() {
// TODO: investigate cases where this inverse technique might not work // TODO: investigate cases where this inverse technique might not work
view.elongateEvents(view.eventsByID[event._id], -dayDelta, -minuteDelta); view.elongateEvents(view.eventsByID[eventId], -dayDelta, -minuteDelta);
view.rerenderEvents(); view.rerenderEvents();
}, ev, ui); }, ev, ui);
view.eventsChanged = true; view.eventsChanged = true;
view.rerenderEvents(); view.rerenderEvents(eventId);
}, },
@ -202,7 +204,7 @@ var viewMethods = {
var view = this; var view = this;
if (!view.options.disableResizing && eventElement.resizable) { if (!view.options.disableResizing && eventElement.resizable) {
eventElement.resizable({ eventElement.resizable({
handles: view.options.isRTL ? 'w' : 'e', handles: view.options.isRTL ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'},
grid: colWidth, grid: colWidth,
minWidth: colWidth/2, // need this or else IE throws errors when too small minWidth: colWidth/2, // need this or else IE throws errors when too small
containment: view.element.parent().parent(), // the main element... containment: view.element.parent().parent(), // the main element...
@ -309,7 +311,6 @@ var viewMethods = {
// event rendering calculation utilities // event rendering calculation utilities
function stackSegs(segs) { function stackSegs(segs) {