Browse Source

Ports sticky/visiblity from 2.x early xmas #2127

pull/2136/merge
jlukic 10 years ago
parent
commit
cd54328cb9
3 changed files with 152 additions and 61 deletions
  1. 11
      RELEASE-NOTES.md
  2. 131
      src/definitions/behaviors/visibility.js
  3. 71
      src/definitions/modules/sticky.js

11
RELEASE-NOTES.md

@ -1,5 +1,16 @@
## RELEASE NOTES
### Version 1.12.0 - April 13, 2015
**Enhancements**
- **Visibility** - Adds updated visibility module from `2.x` channel. Visibility will automatically refresh by default after images load on page refresh. Fixes issues with element positions after image loading.
- **Sticky** - Adds sticky module from `2.x` branch. Sticky elements now use pub/sub with drastically improved performance. Sticky elements that do not fit on page will now scroll at the same speed as the page is scrolled instead of slower.
**Bugs**
- **Build Tools** - Fixes issue with component glob matching twice (causing build to include file twice) if duplicate values found in `semantic.json` component.
- **Input** - Backports fix from `2.x` for `ui fluid input` not appearing correctly.
- **Visibility** - Fixed issue where `precache` behavior was missing from visibility causing `image` lazy loading to fail
### Version 1.11.8 - April 13, 2015
**Bugs**

131
src/definitions/behaviors/visibility.js

@ -42,11 +42,11 @@ $.fn.visibility = function(parameters) {
moduleNamespace = 'module-' + namespace,
$window = $(window),
$module = $(this),
$context = $(settings.context),
$images = $module.find('img'),
selector = $module.selector || '',
instance = $module.data(moduleNamespace),
requestAnimationFrame = window.requestAnimationFrame
@ -76,13 +76,16 @@ $.fn.visibility = function(parameters) {
if(settings.type == 'fixed') {
module.setup.fixed();
}
if(settings.observeChanges) {
module.observeChanges();
}
if( !module.is.visible() ) {
module.error(error.visible, $module);
}
}
if(settings.initialCheck) {
module.checkVisibility();
}
if(settings.observeChanges) {
module.observeChanges();
}
module.instantiate();
},
@ -96,12 +99,18 @@ $.fn.visibility = function(parameters) {
destroy: function() {
module.verbose('Destroying previous module');
if(observer) {
observer.disconnect();
}
$window
.off('load' + eventNamespace, module.event.load)
.off('resize' + eventNamespace, module.event.resize)
;
$context.off('scrollchange' + eventNamespace, module.event.scrollchange);
$module
.off(eventNamespace)
.removeData(moduleNamespace)
;
$window.off('resize' + eventNamespace, module.event.refresh);
$context.off('scroll' + eventNamespace, module.event.scroll);
},
observeChanges: function() {
@ -111,7 +120,10 @@ $.fn.visibility = function(parameters) {
if('MutationObserver' in window) {
observer = new MutationObserver(function(mutations) {
module.verbose('DOM tree modified, updating visibility calculations');
module.refresh();
module.timer = setTimeout(function() {
module.verbose('DOM tree modified, updating sticky menu');
module.refresh();
}, 100);
});
observer.observe(element, {
childList : true,
@ -125,17 +137,19 @@ $.fn.visibility = function(parameters) {
events: function() {
module.verbose('Binding visibility events to scroll and resize');
$window
.on('resize' + eventNamespace, module.event.refresh)
.on('load' + eventNamespace, module.event.load)
.on('resize' + eventNamespace, module.event.resize)
;
// pub/sub pattern
$context
.off('scroll' + eventNamespace)
.on('scroll' + eventNamespace, module.event.scroll)
.on('scrollchange' + eventNamespace, module.event.scrollchange)
;
if($images.length > 0) {
module.bind.imageLoad();
}
},
imageLoad: function() {
var
$images = $module.find('img'),
imageCount = $images.length,
index = imageCount,
loadedCount = 0,
@ -150,44 +164,82 @@ $.fn.visibility = function(parameters) {
}
}
;
$images
.each(function() {
images.push( $(this).attr('src') );
})
;
while(index--) {
cacheImage = document.createElement('img');
cacheImage.onload = handleLoad;
cacheImage.onerror = handleLoad;
cacheImage.src = images[index];
cache.push(cacheImage);
if(imageCount > 0) {
$images
.each(function() {
images.push( $(this).attr('src') );
})
;
while(index--) {
cacheImage = document.createElement('img');
cacheImage.onload = handleLoad;
cacheImage.onerror = handleLoad;
cacheImage.src = images[index];
cache.push(cacheImage);
}
}
}
},
event: {
refresh: function() {
resize: function() {
module.debug('Window resized');
requestAnimationFrame(module.refresh);
},
load: function() {
module.debug('Page finished loading');
requestAnimationFrame(module.refresh);
},
// publishes scrollchange event on one scroll
scroll: function() {
module.verbose('Scroll position changed');
if(settings.throttle) {
clearTimeout(module.timer);
module.timer = setTimeout(function() {
module.checkVisibility();
$context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
}, settings.throttle);
}
else {
requestAnimationFrame(function() {
module.checkVisibility();
$context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
});
}
},
// subscribes to scrollchange
scrollchange: function(event, scrollPosition) {
module.checkVisibility(scrollPosition);
},
},
precache: function(images, callback) {
if (!(images instanceof Array)) {
images = [images];
}
var
imagesLength = images.length,
loadedCounter = 0,
cache = [],
cacheImage = document.createElement('img'),
handleLoad = function() {
loadedCounter++;
if (loadedCounter >= images.length) {
if ($.isFunction(callback)) {
callback();
}
}
}
;
while (imagesLength--) {
cacheImage = document.createElement('img');
cacheImage.onload = handleLoad;
cacheImage.onerror = handleLoad;
cacheImage.src = images[imagesLength];
cache.push(cacheImage);
}
},
should: {
trackChanges: function() {
if(methodInvoked && queryArguments.length > 0) {
if(methodInvoked) {
module.debug('One time query, no need to bind events');
return false;
}
@ -287,7 +339,7 @@ $.fn.visibility = function(parameters) {
},
refresh: function() {
module.debug('Refreshing constants (element width/height)');
module.debug('Refreshing constants (width/height)');
module.reset();
module.save.position();
module.checkVisibility();
@ -302,11 +354,14 @@ $.fn.visibility = function(parameters) {
}
},
checkVisibility: function() {
checkVisibility: function(scroll) {
module.verbose('Checking visibility of element', module.cache.element);
if( module.is.visible() ) {
// save scroll position
module.save.scroll(scroll);
// update calculations derived from scroll
module.save.calculations();
@ -616,7 +671,6 @@ $.fn.visibility = function(parameters) {
save: {
calculations: function() {
module.verbose('Saving all calculations necessary to determine positioning');
module.save.scroll();
module.save.direction();
module.save.screenCalculations();
module.save.elementCalculations();
@ -629,8 +683,9 @@ $.fn.visibility = function(parameters) {
}
}
},
scroll: function() {
module.cache.scroll = $context.scrollTop() + settings.offset;
scroll: function(scrollPosition) {
scrollPosition = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
module.cache.scroll = scrollPosition;
},
direction: function() {
var
@ -855,7 +910,7 @@ $.fn.visibility = function(parameters) {
});
}
clearTimeout(module.performance.timer);
module.performance.timer = setTimeout(module.performance.display, 100);
module.performance.timer = setTimeout(module.performance.display, 500);
},
display: function() {
var
@ -975,6 +1030,9 @@ $.fn.visibility.settings = {
// whether to use mutation observers to follow changes
observeChanges : true,
// whether to refresh calculations after all page images load
refreshOnLoad : true,
// callback should only occur one time
once : true,
@ -1029,9 +1087,10 @@ $.fn.visibility.settings = {
},
error : {
method : 'The method you called is not defined.'
method : 'The method you called is not defined.',
visible : 'Element is hidden, you must call refresh after element becomes visible'
}
};
})( jQuery, window , document );
})( jQuery, window , document );

71
src/definitions/modules/sticky.js

@ -87,13 +87,18 @@ $.fn.sticky = function(parameters) {
},
destroy: function() {
module.verbose('Destroying previous module');
module.verbose('Destroying previous instance');
module.reset();
if(observer) {
observer.disconnect();
}
$window.off('resize' + eventNamespace, module.event.resize);
$scroll.off('scroll' + eventNamespace, module.event.scroll);
$window
.off('load' + eventNamespace, module.event.load)
.off('resize' + eventNamespace, module.event.resize)
;
$scroll
.off('scrollchange' + eventNamespace, module.event.scrollchange)
;
$module.removeData(moduleNamespace);
},
@ -107,7 +112,7 @@ $.fn.sticky = function(parameters) {
module.timer = setTimeout(function() {
module.verbose('DOM tree modified, updating sticky menu');
module.refresh();
}, 20);
}, 100);
});
observer.observe(element, {
childList : true,
@ -147,23 +152,36 @@ $.fn.sticky = function(parameters) {
bind: {
events: function() {
$window.on('resize' + eventNamespace, module.event.resize);
$scroll.on('scroll' + eventNamespace, module.event.scroll);
$window
.on('load' + eventNamespace, module.event.load)
.on('resize' + eventNamespace, module.event.resize)
;
// pub/sub pattern
$scroll
.off('scroll' + eventNamespace)
.on('scroll' + eventNamespace, module.event.scroll)
.on('scrollchange' + eventNamespace, module.event.scrollchange)
;
}
},
event: {
load: function() {
module.verbose('Page contents finished loading');
requestAnimationFrame(module.refresh);
},
resize: function() {
requestAnimationFrame(function() {
module.refresh();
module.stick();
});
module.verbose('Window resized');
requestAnimationFrame(module.refresh);
},
scroll: function() {
requestAnimationFrame(function() {
module.stick();
settings.onScroll.call(element);
$scroll.trigger('scrollchange' + eventNamespace, $scroll.scrollTop() );
});
},
scrollchange: function(event, scrollPosition) {
module.stick(scrollPosition);
settings.onScroll.call(element);
}
},
@ -189,7 +207,7 @@ $.fn.sticky = function(parameters) {
},
save: {
scroll: function(scroll) {
lastScroll: function(scroll) {
module.lastScroll = scroll;
},
positions: function() {
@ -268,6 +286,7 @@ $.fn.sticky = function(parameters) {
: Math.abs(parseInt($module.css('bottom'), 10)) || 0
;
},
elementScroll: function(scroll) {
scroll = scroll || $scroll.scrollTop();
var
@ -275,14 +294,14 @@ $.fn.sticky = function(parameters) {
window = module.cache.window,
delta = module.get.scrollChange(scroll),
maxScroll = (element.height - window.height + settings.offset),
currentScroll = module.get.currentElementScroll(),
possibleScroll = (currentScroll + delta),
elementScroll = module.get.currentElementScroll(),
possibleScroll = (elementScroll + delta),
elementScroll
;
if(module.cache.fits || possibleScroll < 0) {
elementScroll = 0;
}
else if (possibleScroll > maxScroll ) {
else if(possibleScroll > maxScroll ) {
elementScroll = maxScroll;
}
else {
@ -313,8 +332,8 @@ $.fn.sticky = function(parameters) {
$container = $module.offsetParent();
}
else {
module.debug('Settings container size', module.cache.context.height);
if( Math.abs($container.height() - module.cache.context.height) > 5) {
module.debug('Context has padding, specifying exact height for container', module.cache.context.height);
$container.css({
height: module.cache.context.height
});
@ -369,8 +388,9 @@ $.fn.sticky = function(parameters) {
}
},
stick: function() {
stick: function(scroll) {
var
cachedPosition = scroll || $scroll.scrollTop(),
cache = module.cache,
fits = cache.fits,
element = cache.element,
@ -380,11 +400,13 @@ $.fn.sticky = function(parameters) {
? settings.bottomOffset
: settings.offset,
scroll = {
top : $scroll.scrollTop() + offset,
bottom : $scroll.scrollTop() + offset + window.height
top : cachedPosition + offset,
bottom : cachedPosition + offset + window.height
},
direction = module.get.direction(scroll.top),
elementScroll = module.get.elementScroll(scroll.top),
elementScroll = (fits)
? 0
: module.get.elementScroll(scroll.top),
// shorthand
doesntFit = !fits,
@ -392,7 +414,7 @@ $.fn.sticky = function(parameters) {
;
// save current scroll for next run
module.save.scroll(scroll.top);
module.save.lastScroll(scroll.top);
if(elementVisible) {
@ -552,13 +574,12 @@ $.fn.sticky = function(parameters) {
module.unbind();
module.unfix();
module.resetCSS();
module.remove.offset();
},
resetCSS: function() {
$module
.css({
top : '',
bottom : '',
width : '',
height : ''
})
@ -788,4 +809,4 @@ $.fn.sticky.settings = {
};
})( jQuery, window , document );
})( jQuery, window , document );
Loading…
Cancel
Save