Skip to content

Commit

Permalink
fix(util): properly determine viewport top offset
Browse files Browse the repository at this point in the history
* Properly determines the viewport top offset, without any consistency issues for the `scrollTop`.
* The `scrollTop` method had a wrong name when passing an element, because it resolved the client rects in relative to the viewport.
* Now it determines the viewport top offset from the window (instead of using the body element - this is deprecated)

Fixes angular#9370.
  • Loading branch information
devversion committed Aug 30, 2016
1 parent 0cd2a59 commit 095a0e7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 32 deletions.
4 changes: 3 additions & 1 deletion src/components/autocomplete/js/autocompleteController.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
styles.bottom = bot + 'px';
styles.maxHeight = Math.min(MAX_HEIGHT, hrect.top - root.top - MENU_PADDING) + 'px';
} else {
var bottomSpace = root.bottom - hrect.bottom - MENU_PADDING + $mdUtil.getViewportTop();

styles.top = (top - offset) + 'px';
styles.bottom = 'auto';
styles.maxHeight = Math.min(MAX_HEIGHT, root.bottom + $mdUtil.scrollTop() - hrect.bottom - MENU_PADDING) + 'px';
styles.maxHeight = Math.min(MAX_HEIGHT, bottomSpace) + 'px';
}

elements.$.scrollContainer.css(styles);
Expand Down
5 changes: 4 additions & 1 deletion src/components/dialog/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -1164,8 +1164,11 @@ function MdDialogProvider($$interimElementProvider) {
height: container.css('height')
};

// If the body is fixed, determine the position of the parent in relative to the viewport.
var parentTop = $mdUtil.getViewportTop() + options.parent[0].getBoundingClientRect().top;

container.css({
top: (isFixed ? $mdUtil.scrollTop(options.parent) : 0) + 'px',
top: (isFixed ? parentTop : 0) + 'px',
height: height ? height + 'px' : '100%'
});

Expand Down
3 changes: 2 additions & 1 deletion src/components/input/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,8 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout, $mdGesture)

function onDrag(ev) {
if (!isDragging) return;
element.css('height', startHeight + (ev.pointer.y - dragStart) - $mdUtil.scrollTop() + 'px');

element.css('height', (startHeight + ev.pointer.distanceY) + 'px');
}

function onDragEnd(ev) {
Expand Down
52 changes: 24 additions & 28 deletions src/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,12 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in
},

/**
* Calculate the positive scroll offset
* TODO: Check with pinch-zoom in IE/Chrome;
* https://code.google.com/p/chromium/issues/detail?id=496285
* Determines the absolute position of the viewport.
* Useful when making client rectangles absolute.
* @returns {number}
*/
scrollTop: function(element) {
element = angular.element(element || $document[0].body);

var body = (element[0] == $document[0].body) ? $document[0].body : undefined;
var scrollTop = body ? body.scrollTop + body.parentElement.scrollTop : 0;

// Calculate the positive scroll offset
return scrollTop || Math.abs(element[0].getBoundingClientRect().top);
getViewportTop: function() {
return window.scrollY || window.pageYOffset || 0;
},

/**
Expand Down Expand Up @@ -251,37 +245,39 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in

// Converts the body to a position fixed block and translate it to the proper scroll position
function disableBodyScroll() {
var htmlNode = body.parentNode;
var restoreHtmlStyle = htmlNode.style.cssText || '';
var restoreBodyStyle = body.style.cssText || '';
var scrollOffset = $mdUtil.scrollTop(body);
var documentElement = $document[0].documentElement;

var prevDocumentStyle = documentElement.style.cssText || '';
var prevBodyStyle = body.style.cssText || '';

var viewportTop = $mdUtil.getViewportTop();
var clientWidth = body.clientWidth;

if (body.scrollHeight > body.clientHeight + 1) {
applyStyles(body, {

angular.element(body).css({
position: 'fixed',
width: '100%',
top: -scrollOffset + 'px'
top: -viewportTop + 'px'
});

htmlNode.style.overflowY = 'scroll';
documentElement.style.overflowY = 'scroll';
}

if (body.clientWidth < clientWidth) applyStyles(body, {overflow: 'hidden'});
if (body.clientWidth < clientWidth) {
body.style.overflow = 'hidden';
}

return function restoreScroll() {
body.style.cssText = restoreBodyStyle;
htmlNode.style.cssText = restoreHtmlStyle;
body.scrollTop = scrollOffset;
htmlNode.scrollTop = scrollOffset;
// Reset the inline style CSS to the previous.
body.style.cssText = prevBodyStyle;
documentElement.style.cssText = prevDocumentStyle;

// The body loses its scroll position while being fixed.
body.scrollTop = viewportTop;
};
}

function applyStyles(el, styles) {
for (var key in styles) {
el.style[key] = styles[key];
}
}
},
enableScrolling: function() {
var method = this.disableScrollAround._enableScrolling;
Expand Down
25 changes: 24 additions & 1 deletion src/core/util/util.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,33 @@ describe('util', function() {
// Restore the scrolling.
enableScrolling();
window.scrollTo(0, 0);
document.body.removeChild(element[0]);

element.remove();
}));
});

describe('getViewportTop', function() {

it('should properly determine the top offset', inject(function($mdUtil) {
var element = angular.element('<div style="height: 2000px">');

document.body.appendChild(element[0]);

// Scroll down to 1000px y-axis.
window.scrollTo(0, 1000);

expect($mdUtil.getViewportTop()).toBe(1000);

// Restore the scrolling.
window.scrollTo(0, 0);

expect($mdUtil.getViewportTop()).toBe(0);

element.remove();
}));

});

describe('nextTick', function() {
it('should combine multiple calls into a single digest', inject(function($mdUtil, $rootScope, $timeout) {
var digestWatchFn = jasmine.createSpy('watchFn');
Expand Down

0 comments on commit 095a0e7

Please sign in to comment.