Skip to content

Commit

Permalink
Add back to top component
Browse files Browse the repository at this point in the history
  • Loading branch information
NickColley committed Jan 24, 2019
1 parent 2e1e031 commit a1f3133
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BackToTop from './components/back-to-top.js'
import common from 'govuk-frontend/common'
import CookieBanner from './components/cookie-banner.js'
import Example from './components/example.js'
Expand Down Expand Up @@ -39,3 +40,8 @@ new MobileNav().init()
// Initialise search
var $searchContainer = document.querySelector('[data-module="app-search"]')
new Search($searchContainer).init()

// Initialise back to top
var $backToTop = document.querySelector('[data-module="app-back-to-top"]')
var $observedElement = document.querySelector('.app-subnav')
new BackToTop($backToTop, { $observedElement: $observedElement }).init()
62 changes: 62 additions & 0 deletions src/javascripts/components/back-to-top.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'govuk-frontend/vendor/polyfills/Function/prototype/bind'

function BackToTop ($module, options) {
this.$module = $module
this.$observedElement = options.$observedElement
this.intersectionRatio = 0
}

BackToTop.prototype.init = function () {
var $observedElement = this.$observedElement

// If there's no element for the back to top to follow, exit early.
if (!$observedElement) {
return
}

if (!('IntersectionObserver' in window)) {
// If there's no support fallback to regular sticky behaviour
return this.update()
}

// Create new IntersectionObserver
var observer = new window.IntersectionObserver(function (entries) {
// Available data when an intersection happens
// Back to top visibility
// Element enters the viewport
if (entries[0].intersectionRatio !== 0) {
// How much of the element is visible
this.intersectionRatio = entries[0].intersectionRatio
// Element leaves the viewport
} else {
this.intersectionRatio = 0
}
this.update()
}.bind(this), {
// Call the observer, when the element enters the viewport,
// when 25%, 50%, 75% and the whole element are visible
threshold: [0, 0.25, 0.5, 0.75, 1]
})

observer.observe($observedElement)
}

BackToTop.prototype.update = function () {
var thresholdPercent = (this.intersectionRatio * 100)

if (thresholdPercent === 100) {
this.hide()
} else if (thresholdPercent < 90) {
this.show()
}
}

BackToTop.prototype.hide = function () {
this.$module.classList.add('app-back-to-top--hidden')
}

BackToTop.prototype.show = function () {
this.$module.classList.remove('app-back-to-top--hidden')
}

export default BackToTop
21 changes: 21 additions & 0 deletions src/stylesheets/components/_back-to-top.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.app-back-to-top {
position: -webkit-sticky; // Needed for Safari on OSX
position: sticky; // sass-lint:disable-line no-duplicate-properties
top: govuk-spacing(6);
margin-bottom: govuk-spacing(6);
}

.app-back-to-top__icon {
display: inline-block;
width: .8em;
height: 1em;
margin-top: -(govuk-spacing(1));
margin-right: govuk-spacing(2);
vertical-align: middle;
}

@supports (position: sticky) {
.js-enabled .app-back-to-top--hidden {
display: none;
}
}
1 change: 1 addition & 0 deletions src/stylesheets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ $app-light-grey: #f8f8f8;
$app-code-color: #dd1144;

// App-specific components
@import "components/back-to-top";
@import "components/banner";
@import "components/contact-panel";
@import "components/cookie-banner";
Expand Down
1 change: 1 addition & 0 deletions views/layouts/layout-pane.njk
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<div class="app-pane__body govuk-width-container">
<div class="app-pane__subnav app-hide-mobile">
{% include "_subnav.njk" %}
{% include "_back-to-top.njk" %}
</div>
<div class="app-pane__content">
<main id="main-content" class="app-content" role="main">
Expand Down
2 changes: 1 addition & 1 deletion views/layouts/layout.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{% extends "_generic.njk" %}

{% block body %}
<a name="top"></a>
<div class="app-pane__content">
<main id="main-content" role="main">
{{ contents | safe }}
Expand All @@ -10,4 +11,3 @@
</div>

{% endblock %}

9 changes: 9 additions & 0 deletions views/partials/_back-to-top.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{# Safari on OSX with `position: -webkit-sticky` requires a block level element.
To avoid a large focus area we use a wrapper element. #}
<div class="app-back-to-top app-back-to-top--hidden" data-module="app-back-to-top">
<a class="govuk-link govuk-link--no-visited-state" href="#top">
<svg class="app-back-to-top__icon" xmlns="http://www.w3.org/2000/svg" width="13" height="17" viewBox="0 0 13 17">
<path fill="currentColor" d="M6.5 0L0 6.5 1.4 8l4-4v12.7h2V4l4.3 4L13 6.4z"></path>
</svg>Back to top
</a>
</div>

0 comments on commit a1f3133

Please sign in to comment.