mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-24 16:24:08 -04:00
Add stop-at-bottom functionality
Detach all methods from sticky reference so they can be attached to different objects. Split sticky into stickAtTop and stickAtBottom and make new versions of all methods and properties specific to stickAtBottom. Add CSS for stickAtBottom and call on load
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
$(() => $("time.timeago").timeago());
|
$(() => $("time.timeago").timeago());
|
||||||
|
|
||||||
$(() => GOVUK.stickAtTopWhenScrolling.init());
|
$(() => GOVUK.stickAtTopWhenScrolling.init());
|
||||||
|
$(() => GOVUK.stickAtBottomWhenScrolling.init());
|
||||||
|
|
||||||
var showHideContent = new GOVUK.ShowHideContent();
|
var showHideContent = new GOVUK.ShowHideContent();
|
||||||
showHideContent.init();
|
showHideContent.init();
|
||||||
|
|||||||
@@ -5,142 +5,243 @@
|
|||||||
var GOVUK = global.GOVUK || {}
|
var GOVUK = global.GOVUK || {}
|
||||||
|
|
||||||
// Stick elements to top of screen when you scroll past, documentation is in the README.md
|
// Stick elements to top of screen when you scroll past, documentation is in the README.md
|
||||||
var sticky = {
|
var Sticky = function (selector) {
|
||||||
_hasScrolled: false,
|
this._hasScrolled: false
|
||||||
_scrollTimeout: false,
|
this._scrollTimeout: false
|
||||||
_hasResized: false,
|
this._hasResized: false
|
||||||
_resizeTimeout: false,
|
this._resizeTimeout: false
|
||||||
_els: [],
|
this._els: []
|
||||||
|
|
||||||
getWindowDimensions: function () {
|
this.CSS_SELECTOR: selector
|
||||||
return {
|
}
|
||||||
height: $(global).height(),
|
Sticky.prototype.getWindowDimensions = function () {
|
||||||
width: $(global).width()
|
return {
|
||||||
}
|
height: $(global).height(),
|
||||||
},
|
width: $(global).width()
|
||||||
getWindowPositions: function () {
|
}
|
||||||
return {
|
}
|
||||||
scrollTop: $(global).scrollTop()
|
Sticky.prototype.getWindowPositions = function () {
|
||||||
}
|
return {
|
||||||
},
|
scrollTop: $(global).scrollTop()
|
||||||
getElementOffset: function ($el) {
|
}
|
||||||
return $el.offset()
|
}
|
||||||
},
|
Sticky.prototype.setFixedTop = function (el) {
|
||||||
setElHeight: function (el) {
|
var $siblingEl = $('<div></div>')
|
||||||
var fixedOffset = parseInt(el.$fixedEl.css('top'), 10)
|
$siblingEl.insertBefore(el.$fixedEl)
|
||||||
var $el = el.$fixedEl
|
var fixedTop = $siblingEl.offset().top - $siblingEl.position().top
|
||||||
var $img = $el.find('img')
|
$siblingEl.remove()
|
||||||
|
|
||||||
fixedOffset = isNaN(fixedOffset) ? 0 : fixedOffset
|
el.fixedTop = fixedTop
|
||||||
|
}
|
||||||
|
Sticky.prototype.setElHeight = function (el) {
|
||||||
|
var self = this
|
||||||
|
var fixedOffset = parseInt(el.$fixedEl.css('top'), 10)
|
||||||
|
var $el = el.$fixedEl
|
||||||
|
var $img = $el.find('img')
|
||||||
|
|
||||||
if ($img.length > 0) {
|
fixedOffset = isNaN(fixedOffset) ? 0 : fixedOffset
|
||||||
var image = new global.Image()
|
|
||||||
image.onload = function () {
|
if ($img.length > 0) {
|
||||||
el.height = $el.outerHeight() + fixedOffset
|
var image = new global.Image()
|
||||||
}
|
image.onload = function () {
|
||||||
image.src = $img.attr('src')
|
|
||||||
} else {
|
|
||||||
el.height = $el.outerHeight() + fixedOffset
|
el.height = $el.outerHeight() + fixedOffset
|
||||||
|
el.scrolledTo = self.getScrollingTo(el)
|
||||||
}
|
}
|
||||||
},
|
image.src = $img.attr('src')
|
||||||
init: function () {
|
} else {
|
||||||
var $els = $('.js-stick-at-top-when-scrolling')
|
el.height = $el.outerHeight() + fixedOffset
|
||||||
|
el.scrolledTo = self.getScrollingTo(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sticky.prototype.init = function () {
|
||||||
|
var self = this
|
||||||
|
var $els = $(self.CSS_SELECTOR)
|
||||||
|
|
||||||
if ($els.length > 0) {
|
if ($els.length > 0) {
|
||||||
$els.each(function (i, el) {
|
$els.each(function (i, el) {
|
||||||
var $el = $(el)
|
var $el = $(el)
|
||||||
var el = {
|
var el = {
|
||||||
$fixedEl: $el
|
$fixedEl: $el,
|
||||||
}
|
scrolledFrom: self.getScrolledFrom($el),
|
||||||
|
stopped: false
|
||||||
sticky.setElHeight(el)
|
|
||||||
sticky._els.push(el)
|
|
||||||
$el.data('scrolled-from', sticky.getElementOffset($el).top)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (sticky._scrollTimeout === false) {
|
|
||||||
$(global).scroll(sticky.onScroll)
|
|
||||||
sticky._scrollTimeout = global.setInterval(sticky.checkScroll, 50)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sticky._resizeTimeout === false) {
|
self.setFixedTop(el)
|
||||||
$(global).resize(sticky.onResize)
|
self.setElHeight(el)
|
||||||
sticky._resizeTimeout = global.setInterval(sticky.checkResize, 50)
|
self._els.push(el)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
if (self._scrollTimeout === false) {
|
||||||
|
$(global).scroll(function (e) { self.onScroll() })
|
||||||
|
self._scrollTimeout = global.setInterval(function (e) { self.checkScroll() }, 50)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onScroll: function () {
|
|
||||||
sticky._hasScrolled = true
|
|
||||||
},
|
|
||||||
onResize: function () {
|
|
||||||
sticky._hasResized = true
|
|
||||||
},
|
|
||||||
scrolledFromInsideWindow: function (scrolledFrom) {
|
|
||||||
var windowVerticalPosition = sticky.getWindowPositions().scrollTop
|
|
||||||
|
|
||||||
return windowVerticalPosition < scrolledFrom
|
if (self._resizeTimeout === false) {
|
||||||
},
|
$(global).resize(function (e) { self.onResize() })
|
||||||
checkScroll: function () {
|
self._resizeTimeout = global.setInterval(function (e) { self.checkResize() }, 50)
|
||||||
if (sticky._hasScrolled === true) {
|
|
||||||
sticky._hasScrolled = false
|
|
||||||
|
|
||||||
var windowDimensions = sticky.getWindowDimensions()
|
|
||||||
|
|
||||||
$.each(sticky._els, function (i, el) {
|
|
||||||
var $el = el.$fixedEl
|
|
||||||
var scrolledFrom = $el.data('scrolled-from')
|
|
||||||
|
|
||||||
if (scrolledFrom && sticky.scrolledFromInsideWindow(scrolledFrom)) {
|
|
||||||
sticky.release($el)
|
|
||||||
} else if (windowDimensions.width > 768 && !sticky.scrolledFromInsideWindow(scrolledFrom)) {
|
|
||||||
sticky.stick($el)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkResize: function () {
|
|
||||||
if (sticky._hasResized === true) {
|
|
||||||
sticky._hasResized = false
|
|
||||||
|
|
||||||
var windowDimensions = sticky.getWindowDimensions()
|
|
||||||
|
|
||||||
$.each(sticky._els, function (i, el) {
|
|
||||||
var $el = el.$fixedEl
|
|
||||||
|
|
||||||
var elResize = $el.hasClass('js-sticky-resize')
|
|
||||||
if (elResize) {
|
|
||||||
var $shim = $('.shim')
|
|
||||||
var $elParent = $el.parent('div')
|
|
||||||
var elParentWidth = $elParent.width()
|
|
||||||
$shim.css('width', elParentWidth)
|
|
||||||
$el.css('width', elParentWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowDimensions.width <= 768) {
|
|
||||||
sticky.release($el)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addShimForEl: function ($el, width, height) {
|
|
||||||
$el.before('<div class="shim" style="width: ' + width + 'px; height: ' + height + 'px"> </div>')
|
|
||||||
},
|
|
||||||
stick: function ($el) {
|
|
||||||
if (!$el.hasClass('content-fixed')) {
|
|
||||||
var height = Math.max($el.height(), 1)
|
|
||||||
var width = $el.width()
|
|
||||||
|
|
||||||
sticky.addShimForEl($el, width, height)
|
|
||||||
$el.css('width', width + 'px').addClass('content-fixed')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
release: function ($el) {
|
|
||||||
if ($el.hasClass('content-fixed')) {
|
|
||||||
$el.removeClass('content-fixed').css('width', '')
|
|
||||||
$el.siblings('.shim').remove()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GOVUK.stickAtTopWhenScrolling = sticky
|
Sticky.prototype.onScroll = function () {
|
||||||
|
this._hasScrolled = true
|
||||||
|
}
|
||||||
|
Sticky.prototype.onResize = function () {
|
||||||
|
this._hasResized = true
|
||||||
|
}
|
||||||
|
Sticky.prototype.viewportIsWideEnough = function (windowWidth) {
|
||||||
|
return windowWidth > 768
|
||||||
|
}
|
||||||
|
Sticky.prototype.checkScroll = function () {
|
||||||
|
var self = this
|
||||||
|
|
||||||
|
if (self._hasScrolled === true) {
|
||||||
|
self._hasScrolled = false
|
||||||
|
|
||||||
|
$.each(self._els, function (i, el) {
|
||||||
|
var $el = el.$fixedEl
|
||||||
|
|
||||||
|
var windowDimensions = self.getWindowDimensions()
|
||||||
|
|
||||||
|
if (self.scrolledFromInsideWindow(el.scrolledFrom)) {
|
||||||
|
self.release($el)
|
||||||
|
} else {
|
||||||
|
if (self.scrolledToOutsideWindow(el, windowDimensions.height)) {
|
||||||
|
self.stop(el)
|
||||||
|
} else if (self.viewportIsWideEnough(windowDimensions.width)) {
|
||||||
|
if (el.stopped) {
|
||||||
|
self.unstop(el)
|
||||||
|
}
|
||||||
|
self.stick($el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sticky.prototype.checkResize = function () {
|
||||||
|
var self = this
|
||||||
|
|
||||||
|
if (self._hasResized === true) {
|
||||||
|
self._hasResized = false
|
||||||
|
|
||||||
|
var windowDimensions = self.getWindowDimensions()
|
||||||
|
|
||||||
|
$.each(self._els, function (i, el) {
|
||||||
|
var $el = el.$fixedEl
|
||||||
|
|
||||||
|
var elResize = $el.hasClass('js-self-resize')
|
||||||
|
if (elResize) {
|
||||||
|
var $shim = $('.shim')
|
||||||
|
var $elParent = $el.parent('div')
|
||||||
|
var elParentWidth = $elParent.width()
|
||||||
|
$shim.css('width', elParentWidth)
|
||||||
|
$el.css('width', elParentWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowDimensions.width <= 768) {
|
||||||
|
self.release($el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sticky.prototype.stick = function ($el) {
|
||||||
|
if (!$el.hasClass('content-fixed')) {
|
||||||
|
var height = Math.max($el.height(), 1)
|
||||||
|
var width = $el.width()
|
||||||
|
|
||||||
|
this.addShimForEl($el, width, height)
|
||||||
|
$el.css('width', width + 'px').addClass('content-fixed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sticky.prototype.release = function ($el) {
|
||||||
|
if ($el.hasClass('content-fixed')) {
|
||||||
|
$el.removeClass('content-fixed').css('width', '')
|
||||||
|
$el.siblings('.shim').remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stickAtTop = new Sticky('.js-stick-at-top-when-scrolling')
|
||||||
|
stickAtTop.getScrolledFrom = function ($el) {
|
||||||
|
return $el.offset().top
|
||||||
|
}
|
||||||
|
stickAtTop.getScrollingTo = function (el) {
|
||||||
|
var footer = $('.js-footer:eq(0)')
|
||||||
|
if (footer.length === 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (footer.offset().top - 10) - el.height
|
||||||
|
}
|
||||||
|
stickAtTop.scrolledFromInsideWindow = function (scrolledFrom) {
|
||||||
|
var windowTop = this.getWindowPositions().scrollTop
|
||||||
|
|
||||||
|
return scrolledFrom > windowTop
|
||||||
|
}
|
||||||
|
stickAtTop.scrolledToOutsideWindow = function (el, windowHeight) {
|
||||||
|
var windowTop = this.getWindowPositions().scrollTop
|
||||||
|
|
||||||
|
return windowTop > el.scrolledTo
|
||||||
|
}
|
||||||
|
stickAtTop.addShimForEl = function ($el, width, height) {
|
||||||
|
$el.before('<div class="shim" style="width: ' + width + 'px height: ' + height + 'px"> </div>')
|
||||||
|
}
|
||||||
|
stickAtTop.stop = function (el) {
|
||||||
|
if (!el.stopped) {
|
||||||
|
el.$fixedEl.css({ 'position': 'absolute', 'top': el.scrolledTo })
|
||||||
|
el.stopped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stickAtTop.unstop = function (el) {
|
||||||
|
if (el.stopped) {
|
||||||
|
el.$fixedEl.css({ 'position': '', 'top': '' })
|
||||||
|
el.stopped = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stickAtBottom = new Sticky('.js-stick-at-bottom-when-scrolling')
|
||||||
|
stickAtBottom.getScrolledFrom = function ($el) {
|
||||||
|
return $el.offset().top + $el.outerHeight()
|
||||||
|
}
|
||||||
|
stickAtBottom.getScrollingTo = function (el) {
|
||||||
|
var header = $('.js-header:eq(0)')
|
||||||
|
if (header.length === 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (header.offset().top + header.outerHeight() + 10) + el.height
|
||||||
|
}
|
||||||
|
stickAtBottom.scrolledFromInsideWindow = function (scrolledFrom) {
|
||||||
|
var windowBottom = this.getWindowPositions().scrollTop + this.getWindowDimensions().height
|
||||||
|
|
||||||
|
return scrolledFrom < windowBottom
|
||||||
|
}
|
||||||
|
stickAtBottom.scrolledToOutsideWindow = function (el, windowHeight) {
|
||||||
|
var windowBottom = this.getWindowPositions().scrollTop + this.getWindowDimensions().height
|
||||||
|
|
||||||
|
return windowBottom < el.scrolledTo
|
||||||
|
}
|
||||||
|
stickAtBottom.addShimForEl = function ($el, width, height) {
|
||||||
|
$el.after('<div class="shim" style="width: ' + width + 'px height: ' + height + 'px"> </div>')
|
||||||
|
}
|
||||||
|
stickAtBottom.stop = function (el) {
|
||||||
|
if (!el.stopped) {
|
||||||
|
el.$fixedEl.css({
|
||||||
|
'position': 'absolute',
|
||||||
|
'top': (el.scrolledTo - el.height),
|
||||||
|
'bottom': 'auto'
|
||||||
|
})
|
||||||
|
el.stopped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stickAtBottom.unstop = function (el) {
|
||||||
|
if (el.stopped) {
|
||||||
|
el.$fixedEl.css({
|
||||||
|
'position': '',
|
||||||
|
'top': '',
|
||||||
|
'bottom': ''
|
||||||
|
})
|
||||||
|
el.stopped = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GOVUK.stickAtTopWhenScrolling = stickAtTop
|
||||||
|
GOVUK.stickAtBottomWhenScrolling = stickAtBottom
|
||||||
global.GOVUK = GOVUK
|
global.GOVUK = GOVUK
|
||||||
})(window)
|
})(window)
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
// CSS adapted from
|
// CSS adapted from
|
||||||
// https://github.com/alphagov/govuk_frontend_toolkit/blob/d9489a987086471fe30b4b925a81c12cd198c91d/docs/javascript.md#stick-at-top-when-scrolling
|
// https://github.com/alphagov/govuk_frontend_toolkit/blob/d9489a987086471fe30b4b925a81c12cd198c91d/docs/javascript.md#stick-at-top-when-scrolling
|
||||||
|
|
||||||
.js-stick-at-top-when-scrolling {
|
.js-stick-at-top-when-scrolling,
|
||||||
|
.js-stick-at-bottom-when-scrolling {
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-left: -$gutter-half;
|
margin-left: -$gutter-half;
|
||||||
margin-top: -10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
padding: 10px 0 0 $gutter-half;
|
padding: 10px 0 0 $gutter-half;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 5px;
|
|
||||||
transition: top 0.1s ease-out, box-shadow 1s ease-in-out;
|
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@@ -32,17 +29,27 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.js-stick-at-top-when-scrolling {
|
||||||
|
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
top: 5px;
|
||||||
|
transition: top 0.1s ease-out, box-shadow 1s ease-in-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-stick-at-top-when-scrolling {
|
||||||
|
|
||||||
|
transition: bottom 0.1s ease-out, box-shadow 1s ease-in-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.content-fixed {
|
.content-fixed {
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
|
||||||
background: $white;
|
background: $white;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
margin-top: 0;
|
|
||||||
padding-right: $gutter-half;
|
padding-right: $gutter-half;
|
||||||
border-bottom: 1px solid $border-colour;
|
|
||||||
box-shadow: 0 2px 0 0 rgba($border-colour, 0.2);
|
|
||||||
transition: background 0.6s ease-in-out, margin-top 0.4s ease-out;
|
|
||||||
|
|
||||||
.back-to-top-link {
|
.back-to-top-link {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -51,6 +58,26 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.js-stick-at-top-when-scrolling.content-fixed {
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
border-bottom: 1px solid $border-colour;
|
||||||
|
box-shadow: 0 2px 0 0 rgba($border-colour, 0.2);
|
||||||
|
transition: background 0.6s ease-in-out, margin-top 0.4s ease-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.js-stick-at-bottom-when-scrolling.content-fixed {
|
||||||
|
|
||||||
|
top: auto; // cancel `top: 0;` inherited from govuk-template
|
||||||
|
bottom: 0;
|
||||||
|
border-top: 1px solid $border-colour;
|
||||||
|
box-shadow: 0 -2px 0 0 rgba($border-colour, 0.2);
|
||||||
|
transition: background 0.6s ease-in-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.shim {
|
.shim {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|||||||
Reference in New Issue
Block a user