diff --git a/app/assets/javascripts/modules/sticky-element-container.js b/app/assets/javascripts/modules/sticky-element-container.js index 0357b70ef..114011c2c 100644 --- a/app/assets/javascripts/modules/sticky-element-container.js +++ b/app/assets/javascripts/modules/sticky-element-container.js @@ -7,111 +7,112 @@ Use 'data-module="sticky-element-container"' to instantiate, and add `[data-sticky-element]` to the child you want to position. */ + +window.GOVUK = window.GOVUK || {} +window.GOVUK.Modules = window.GOVUK.Modules || {}; + (function (Modules) { - 'use strict' + function StickyElementContainer (element) { + this.wrapper = element + this.stickyElement = this.wrapper.querySelector('[data-sticky-element]') + this.hasResized = true + this.hasScrolled = true + this.interval = 50 + this.windowVerticalPosition = 1 + this.startPosition = 0 + this.stopPosition = 0 + } + + StickyElementContainer.prototype.init = function () { + if (!this.stickyElement) return + + window.onresize = this.onResize + window.onscroll = this.onScroll + setInterval(this.checkResize.bind(this), this.interval) + setInterval(this.checkScroll.bind(this), this.interval) + this.checkResize() + this.checkScroll() + this.stickyElement.classList.add('sticky-element--enabled') + } + + StickyElementContainer.prototype.getWindowDimensions = function () { + return { + height: window.innerHeight, + width: window.innerWidth + } + } + + StickyElementContainer.prototype.getWindowPositions = function () { + return { + scrollTop: window.scrollY + } + } + + StickyElementContainer.prototype.onResize = function () { + this.hasResized = true + } + + StickyElementContainer.prototype.onScroll = function () { + this.hasScrolled = true + } - Modules.StickyElementContainer = function () { - var self = this + StickyElementContainer.prototype.checkResize = function () { + if (this.hasResized) { + this.hasResized = false + this.hasScrolled = true - self.getWindowDimensions = function () { - return { - height: window.innerHeight, - width: window.innerWidth - } + var windowDimensions = this.getWindowDimensions() + var elementHeight = this.wrapper.offsetHeight || parseFloat(this.wrapper.style.height.replace('px', '')) + this.startPosition = this.wrapper.offsetTop + this.stopPosition = this.wrapper.offsetTop + elementHeight - windowDimensions.height } + } + + StickyElementContainer.prototype.checkScroll = function () { + if (this.hasScrolled) { + this.hasScrolled = false + this.hasResized = true + + this.windowVerticalPosition = this.getWindowPositions().scrollTop + + this.updateVisibility() + this.updatePosition() + } + } - self.getWindowPositions = function () { - return { - scrollTop: window.scrollY - } + StickyElementContainer.prototype.updateVisibility = function () { + var isPastStart = this.startPosition < this.windowVerticalPosition + if (isPastStart) { + this.show() + } else { + this.hide() } + } - self.start = function ($el) { - var wrapper = $el[0] - var stickyElement = wrapper.querySelector('[data-sticky-element]') - - var hasResized = true - var hasScrolled = true - var interval = 50 - var windowVerticalPosition = 1 - var startPosition, stopPosition - - initialise() - - function initialise () { - window.onresize = onResize - window.onscroll = onScroll - setInterval(checkResize, interval) - setInterval(checkScroll, interval) - checkResize() - checkScroll() - stickyElement.classList.add('sticky-element--enabled') - } - - function onResize () { - hasResized = true - } - - function onScroll () { - hasScrolled = true - } - - function checkResize () { - if (hasResized) { - hasResized = false - hasScrolled = true - - var windowDimensions = self.getWindowDimensions() - var elementHeight = wrapper.offsetHeight || parseFloat(wrapper.style.height.replace('px', '')) - startPosition = wrapper.offsetTop - stopPosition = wrapper.offsetTop + elementHeight - windowDimensions.height - } - } - - function checkScroll () { - if (hasScrolled) { - hasScrolled = false - - windowVerticalPosition = self.getWindowPositions().scrollTop - - updateVisibility() - updatePosition() - } - } - - function updateVisibility () { - var isPastStart = startPosition < windowVerticalPosition - if (isPastStart) { - show() - } else { - hide() - } - } - - function updatePosition () { - var isPastEnd = stopPosition < windowVerticalPosition - if (isPastEnd) { - stickToParent() - } else { - stickToWindow() - } - } - - function stickToWindow () { - stickyElement.classList.add('sticky-element--stuck-to-window') - } - - function stickToParent () { - stickyElement.classList.remove('sticky-element--stuck-to-window') - } - - function show () { - stickyElement.classList.remove('sticky-element--hidden') - } - - function hide () { - stickyElement.classList.add('sticky-element--hidden') - } + StickyElementContainer.prototype.updatePosition = function () { + var isPastEnd = this.stopPosition < this.windowVerticalPosition + if (isPastEnd) { + this.stickToParent() + } else { + this.stickToWindow() } } + + StickyElementContainer.prototype.stickToWindow = function () { + this.stickyElement.classList.add('sticky-element--stuck-to-window') + } + + StickyElementContainer.prototype.stickToParent = function () { + this.stickyElement.classList.remove('sticky-element--stuck-to-window') + } + + StickyElementContainer.prototype.show = function () { + this.stickyElement.classList.remove('sticky-element--hidden') + } + + StickyElementContainer.prototype.hide = function () { + this.stickyElement.classList.add('sticky-element--hidden') + } + + Modules.StickyElementContainer = StickyElementContainer })(window.GOVUK.Modules) diff --git a/spec/javascripts/modules/sticky-element-container.spec.js b/spec/javascripts/modules/sticky-element-container.spec.js index 35356892a..8c22ec286 100644 --- a/spec/javascripts/modules/sticky-element-container.spec.js +++ b/spec/javascripts/modules/sticky-element-container.spec.js @@ -2,62 +2,72 @@ describe('A sticky-element-container module', function () { 'use strict' var GOVUK = window.GOVUK - var $ = window.$ - var instance - beforeEach(function () { - instance = new GOVUK.Modules.StickyElementContainer() - }) + describe('on desktop', function () { + var $element + var $footer + var instance + + beforeEach(function () { + $element = $( + '
' + + '
' + + 'Content' + + '
' + + '
' + ) + + instance = new GOVUK.Modules.StickyElementContainer($element[0]) + $footer = $element.find('[data-sticky-element]') + + instance.getWindowDimensions = function () { + return { + height: 768, + width: 1024 + } + } + }) - describe('in a large parent element', function () { - var $element = $( - '
' + - '
' + - 'Content' + - '
' + - '
' - ) - var $footer = $element.find('[data-sticky-element]') - - describe('on desktop', function () { - beforeEach(function () { - instance.getWindowDimensions = function () { - return { - height: 768, - width: 1024 - } + it('hides the element, when scrolled at the top', function () { + instance.getWindowPositions = function () { + return { + scrollTop: 0 } - }) + } - it('hides the element, when scrolled at the top', function () { - instance.start($element) + instance.checkResize() + instance.checkScroll() - expect($footer.hasClass('sticky-element--hidden')).toBe(true) - }) + expect($footer.hasClass('sticky-element--hidden')).toBe(true) + expect($footer.hasClass('sticky-element--stuck-to-window')).toBe(true) + }) - it('shows the element, stuck to the window, when scrolled in the middle', function () { - instance.getWindowPositions = function () { - return { - scrollTop: 5000 - } + it('shows the element, stuck to the window, when scrolled in the middle', function () { + instance.getWindowPositions = function () { + return { + scrollTop: 5000 } - instance.start($element) + } - expect($footer.hasClass('sticky-element--hidden')).toBe(false) - expect($footer.hasClass('sticky-element--stuck-to-window')).toBe(true) - }) + instance.checkResize() + instance.checkScroll() - it('shows the element, stuck to the parent, when scrolled at the bottom', function () { - instance.getWindowPositions = function () { - return { - scrollTop: 9800 - } + expect($footer.hasClass('sticky-element--hidden')).toBe(false) + expect($footer.hasClass('sticky-element--stuck-to-window')).toBe(true) + }) + + it('shows the element, stuck to the parent, when scrolled at the bottom', function () { + instance.getWindowPositions = function () { + return { + scrollTop: 9800 } - instance.start($element) + } + + instance.checkResize() + instance.checkScroll() - expect($footer.hasClass('sticky-element--hidden')).toBe(false) - expect($footer.hasClass('sticky-element--stuck-to-window')).toBe(false) - }) + expect($footer.hasClass('sticky-element--hidden')).toBe(false) + expect($footer.hasClass('sticky-element--stuck-to-window')).toBe(false) }) }) })