Merge pull request #2702 from alphagov/fix-bugs-with-sticky-overflow-code

Fix bugs with sticky overflow code
This commit is contained in:
Tom Byers
2019-01-31 13:55:45 +00:00
committed by GitHub
3 changed files with 61 additions and 28 deletions
@@ -140,7 +140,16 @@
// Object collecting together methods for treating sticky elements as if they // Object collecting together methods for treating sticky elements as if they
// were wrapped by a dialog component // were wrapped by a dialog component
var dialog = { var dialog = {
_hasResized: false, hasResized: false,
// we add padding of 20px around each sticky to isolate it from the rest of the page
// it shouldn't apply between stickys when stacked
_getPaddingBetweenEls: function (els) {
var spaceBetween = 40;
if (els.length < 2) { return 0; }
return (els.length - 1) * spaceBetween;
},
_getTotalHeight: function (els) { _getTotalHeight: function (els) {
var reducer = function (accumulator, currentValue) { var reducer = function (accumulator, currentValue) {
return accumulator + currentValue; return accumulator + currentValue;
@@ -152,6 +161,7 @@
}, },
getOffsetFromEdge: function (el, sticky) { getOffsetFromEdge: function (el, sticky) {
var els = this._elsThatCanBeStuck(sticky._els).slice(); var els = this._elsThatCanBeStuck(sticky._els).slice();
var elsBetween;
var elIdx; var elIdx;
// els must be arranged furtherest from window edge is stuck to first // els must be arranged furtherest from window edge is stuck to first
@@ -165,10 +175,12 @@
// if next to window edge the dialog is stuck to, no offset // if next to window edge the dialog is stuck to, no offset
if (elIdx === (els.length - 1)) { return 0; } if (elIdx === (els.length - 1)) { return 0; }
// make els all those from this one to the window edge
els = els.slice(elIdx);
// get all els between this one and the window edge // get all els between this one and the window edge
els = els.slice(elIdx + 1); elsBetween = els.slice(1);
return this._getTotalHeight(els); return this._getTotalHeight(elsBetween) - this._getPaddingBetweenEls(els);
}, },
getOffsetFromEnd: function (el, sticky) { getOffsetFromEnd: function (el, sticky) {
var els = this._elsThatCanBeStuck(sticky._els).slice(); var els = this._elsThatCanBeStuck(sticky._els).slice();
@@ -218,10 +230,8 @@
sticky.reset(currentEl); sticky.reset(currentEl);
currentEl.canBeStuck(false); currentEl.canBeStuck(false);
if (!sticky._hasResized) { sticky._hasResized = true; } if (!self.hasResized) { self.hasResized = true; }
} }
return this._getTotalHeight(els);
}, },
getElementAtStickyEdge: function (sticky) { getElementAtStickyEdge: function (sticky) {
var els = this._elsThatCanBeStuck(sticky._els); var els = this._elsThatCanBeStuck(sticky._els);
@@ -251,7 +261,7 @@
$(window).scrollTop(this.getInPageEdgePosition(sticky) - windowHeight); $(window).scrollTop(this.getInPageEdgePosition(sticky) - windowHeight);
} }
sticky._hasResized = false; self.hasResized = false;
}, },
releaseEl: function (el, sticky) { releaseEl: function (el, sticky) {
el.$fixedEl.css(sticky.edge, ''); el.$fixedEl.css(sticky.edge, '');
@@ -263,7 +273,7 @@
var Sticky = function (selector) { var Sticky = function (selector) {
this._hasScrolled = false; this._hasScrolled = false;
this._scrollTimeout = false; this._scrollTimeout = false;
this._hasResized = false; this._windowHasResized = false;
this._resizeTimeout = false; this._resizeTimeout = false;
this._elsLoaded = false; this._elsLoaded = false;
this._initialPositionsSet = false; this._initialPositionsSet = false;
@@ -272,6 +282,9 @@
this.CSS_SELECTOR = selector; this.CSS_SELECTOR = selector;
this.STOP_PADDING = 10; this.STOP_PADDING = 10;
}; };
Sticky.prototype.setMode = function (mode) {
_mode = mode;
};
Sticky.prototype.getWindowDimensions = function () { Sticky.prototype.getWindowDimensions = function () {
return { return {
height: $(global).height(), height: $(global).height(),
@@ -360,19 +373,19 @@
} }
}; };
// Recalculate stored dimensions for all sticky elements // Recalculate stored dimensions for all sticky elements
Sticky.prototype.recalculate = function (opts) { Sticky.prototype.recalculate = function () {
var self = this; var self = this;
var onSyncComplete = function () { var onSyncComplete = function () {
self.setEvents(); self.setEvents();
if (_mode === 'dialog') { if (_mode === 'dialog') {
dialog.fitToHeight(self); dialog.fitToHeight(self);
dialog.adjustForResize(self); if (dialog.hasResized) {
dialog.adjustForResize(self);
}
} }
self.setElementPositions(); self.setElementPositions();
}; };
if ((opts !== undefined) && ('mode' in opts)) { _mode = opts.mode; }
this.syncWithDOM(onSyncComplete); this.syncWithDOM(onSyncComplete);
}; };
Sticky.prototype.setElWidth = function (el) { Sticky.prototype.setElWidth = function (el) {
@@ -413,30 +426,38 @@
Sticky.prototype.allElementsLoaded = function (totalEls) { Sticky.prototype.allElementsLoaded = function (totalEls) {
return this._els.length === totalEls; return this._els.length === totalEls;
}; };
Sticky.prototype.hasEl = function (node) { Sticky.prototype.getElForNode = function (node) {
return !!$.grep(this._els, function (el) { return el.$fixedEl.is(node); }).length; var matches = $.grep(this._els, function (el) { return el.$fixedEl.is(node); });
return !!matches.length ? matches[0] : false;
}; };
Sticky.prototype.add = function (el, setPositions, cb) { Sticky.prototype.add = function (el, setPositions, cb) {
var self = this; var self = this;
var $el = $(el); var $el = $(el);
var onDimensionsSet; var onDimensionsSet;
var elObj; var elObj = this.getElForNode(el);
var exists = !!elObj;
// guard against adding elements already stored
if (this.hasEl(el)) { return; }
onDimensionsSet = function () { onDimensionsSet = function () {
elObj.hasLoaded(true); elObj.hasLoaded(true);
self._els.push(elObj);
// guard against adding elements already stored
if (!exists) {
self._els.push(elObj);
}
if (setPositions) { if (setPositions) {
self.setElementPositions(); self.setElementPositions();
} }
if (cb !== undefined) { if (cb !== undefined) {
cb(); cb();
} }
}; };
elObj = new StickyElement($el, self); if (!exists) {
elObj = new StickyElement($el, self);
}
self.setElementDimensions(elObj, onDimensionsSet); self.setElementDimensions(elObj, onDimensionsSet);
}; };
@@ -485,8 +506,8 @@
}); });
} }
}; };
Sticky.prototype.init = function (opts) { Sticky.prototype.init = function () {
this.recalculate(opts); this.recalculate();
}; };
Sticky.prototype.setEvents = function () { Sticky.prototype.setEvents = function () {
var self = this; var self = this;
@@ -511,7 +532,7 @@
this._hasScrolled = true; this._hasScrolled = true;
}; };
Sticky.prototype.onResize = function () { Sticky.prototype.onResize = function () {
this._hasResized = true; this._windowHasResized = true;
}; };
Sticky.prototype.checkScroll = function () { Sticky.prototype.checkScroll = function () {
var self = this; var self = this;
@@ -525,8 +546,8 @@
var self = this, var self = this,
windowWidth = self.getWindowDimensions().width; windowWidth = self.getWindowDimensions().width;
if (self._hasResized === true) { if (self._windowHasResized === true) {
self._hasResized = false; self._windowHasResized = false;
$.each(self._els, function (i, el) { $.each(self._els, function (i, el) {
if (!self.viewportIsWideEnough(windowWidth)) { if (!self.viewportIsWideEnough(windowWidth)) {
@@ -539,7 +560,9 @@
if (self.viewportIsWideEnough(windowWidth)) { if (self.viewportIsWideEnough(windowWidth)) {
if (_mode === 'dialog') { if (_mode === 'dialog') {
dialog.fitToHeight(self); dialog.fitToHeight(self);
dialog.adjustForResize(self); if (dialog.hasResized) {
dialog.adjustForResize(self);
}
} }
self.setElementPositions(); self.setElementPositions();
} }
+8 -2
View File
@@ -139,14 +139,20 @@
}; };
this.render = function() { this.render = function() {
var mode = 'default';
// detach everything, unless they are the currentState // detach everything, unless they are the currentState
this.states.forEach( this.states.forEach(
state => (state.key === this.currentState ? this.$stickyBottom.append(state.$el) : state.$el.detach()) state => (state.key === this.currentState ? this.$stickyBottom.append(state.$el) : state.$el.detach())
); );
// make sticky JS recalculate its cache of the element's position
// use dialog mode for states which contain more than one form control // use dialog mode for states which contain more than one form control
GOVUK.stickAtBottomWhenScrolling.recalculate({ 'mode': 'dialog' }); if (['move-to-existing-folder', 'add-new-template'].includes(this.currentState)) {
mode = 'dialog';
}
GOVUK.stickAtBottomWhenScrolling.setMode(mode);
// make sticky JS recalculate its cache of the element's position
GOVUK.stickAtBottomWhenScrolling.recalculate();
}; };
this.nothingSelectedButtons = $(` this.nothingSelectedButtons = $(`
@@ -46,6 +46,10 @@
padding: $vertical-padding 0 $vertical-padding $gutter-half; padding: $vertical-padding 0 $vertical-padding $gutter-half;
margin-top: -$vertical-padding; margin-top: -$vertical-padding;
& + .js-stick-at-bottom-when-scrolling {
margin-top: ($vertical-padding * 2) * -1;
}
.page-footer { .page-footer {
margin-bottom: 0; margin-bottom: 0;
min-height: 50px; min-height: 50px;