<section class="course-overview" data-behavior="course-overview">
<header>
<h2>Course Overview</h2>
</header>
<article data-brand="mens" class="course-overview__inner">
<a class="course-overview_show-hide"></a>
<div class="info-title-sticky">Introduction to Coaching Football</div>
<div class="course-overview__info">
<div>
<p>Course type:</p>
<h3>Online Only</h3>
</div>
<div class="sticky-price">
<h3>£160</h3>
</div>
</div>
<div class="course-overview__info">
<p>Duration:</p>
<h3>12 Months</h3>
<p>
The taught element of this course is 12 days in total: <br>6 x 2 day modules<br>3 day in-situ support visits (minimum)<br>
</p>
</div>
<div class="course-overview__info">
<p>Leading To:</p>
<h3>FA Level 5 (UEFA Pro)</h3>
<p>in Coaching Football</p>
</div>
<div class="course-overview__info">
<div>
<p>Price:</p>
<h3 class="course-overview__price">£160</h3>
</div>
<a class="cta cta--efl " id="efl-online-book-course" tabindex="0">Book Course</a>
</div>
</article>
</section>
No notes defined.
{
"venue": "Online",
"isOnline": true,
"duration": "12 Months",
"price": 160,
"leadingTo": {
"name": "FA Level 5 (UEFA Pro)",
"category": "in Coaching Football"
},
"content": [
"The taught element of this course is 12 days in total: ",
"6 x 2 day modules",
"3 day in-situ support visits (minimum)"
],
"course-overview-cta": {
"copy": "Book Course",
"modifier": "efl",
"id": "efl-online-book-course",
"href": "",
"additionalClass": ""
}
}
import { gsap } from 'gsap';
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';
export default parentElement => {
const bookCourse = parentElement.querySelector('#efl-online-book-course');
const findCOurse = parentElement.querySelector('#efl-offline-find-course');
const courseAvail = document.querySelector(
'[data-behavior=course-availability]'
);
const courseAvail2 = document.querySelector('.membership-signpost');
const boxWidth = parentElement.clientWidth;
const boxHeight = parentElement.clientHeight;
const MOBILE_BREAKPOINT = 820;
const mobTouch = parentElement.querySelector('.course-overview_show-hide');
const mobSwipe = parentElement.querySelector('.course-overview__inner');
const defaultWidth = window.innerWidth;
gsap.registerPlugin(ScrollToPlugin);
const courseJourneyTwo = () => {
if (!courseAvail) {
return false;
}
gsap.to(window, {
duration: 5,
scrollTo: { y: courseAvail.offsetTop },
ease: 'power2',
});
// window.scroll(0, courseAvail.offsetTop);
return true;
};
const courseJourneyFour = () => {
if (!courseAvail2) {
return false;
}
gsap.to(window, {
duration: 5,
scrollTo: { y: courseAvail2.offsetTop },
ease: 'power2',
});
// window.scroll(0, courseAvail2.offsetTop);
return true;
};
const setContainerSize = () => {
// eslint-disable-next-line no-param-reassign
parentElement.style.width = `${boxWidth}px`;
// eslint-disable-next-line no-param-reassign
parentElement.style.height = `${boxHeight}px`;
};
const findPos = obj => {
// eslint-disable-next-line no-unused-vars
let curleft = 0;
let curtop = 0;
if (obj.offsetParent) {
curleft = obj.offsetLeft;
curtop = obj.offsetTop;
// eslint-disable-next-line no-param-reassign, no-cond-assign
while ((obj = obj.offsetParent)) {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
}
}
return curtop;
};
if (bookCourse) {
if (courseAvail) {
const titleTxt = courseAvail.querySelector('h2').innerText;
bookCourse.setAttribute(
'aria-label',
`${bookCourse.innerText} jump to ${titleTxt} section`
);
} else {
bookCourse.setAttribute(
'aria-label',
`Course overview - ${bookCourse.innerText}`
);
}
bookCourse.addEventListener('click', () => courseJourneyTwo());
bookCourse.addEventListener('keyup', event => {
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
bookCourse.click();
}
});
}
if (findCOurse) {
if (courseAvail2) {
const titleTxt2 = courseAvail2.querySelector('h3').innerText;
findCOurse.setAttribute(
'aria-label',
`${findCOurse.innerText} jump to ${titleTxt2} section`
);
} else {
findCOurse.setAttribute(
'aria-label',
`Course overview - ${findCOurse.innerText}`
);
}
findCOurse.addEventListener('click', () => courseJourneyFour());
findCOurse.addEventListener('keyup', event => {
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
findCOurse.click();
}
});
}
setContainerSize();
// eslint-disable-next-line complexity
window.addEventListener('scroll', () => {
const courseOverviewInner = parentElement.querySelector(
'.course-overview__inner'
);
if (window.innerWidth > MOBILE_BREAKPOINT) {
if (
parentElement.querySelector('article').getAttribute('aria-expanded') ===
'true'
) {
mobTouch.innerHTML = 'Hide Course Overview';
} else {
mobTouch.innerHTML = 'Show Course Overview';
}
} else {
mobTouch.innerHTML = null;
}
if (findPos(parentElement) + parentElement.clientHeight < window.scrollY) {
if (
!parentElement.classList.contains('sticky') &&
findPos(parentElement) + parentElement.clientHeight + 200 >
window.scrollY
) {
parentElement.classList.add('sliding');
}
parentElement.classList.add('sticky');
if (
!parentElement.querySelector('article').getAttribute('aria-expanded')
) {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'false');
}
setTimeout(() => {
parentElement.classList.remove('sliding');
}, 305);
} else {
parentElement.classList.remove('sliding');
parentElement.classList.remove('sticky');
// eslint-disable-next-line no-param-reassign
parentElement.querySelector('article').removeAttribute('aria-expanded');
}
// Removing Course Overview
// landing page, default variant
if (courseAvail) {
if (
window.innerWidth > MOBILE_BREAKPOINT &&
window.scrollY + window.innerHeight > findPos(courseAvail)
) {
parentElement.classList.remove('sticky');
} else if (
window.innerWidth < MOBILE_BREAKPOINT &&
window.scrollY + window.innerHeight > findPos(courseAvail)
) {
parentElement.classList.remove('sticky');
}
}
// landing page, find-course variant
if (courseAvail2) {
if (
window.innerWidth > MOBILE_BREAKPOINT &&
window.scrollY >
findPos(courseAvail2) - courseOverviewInner.clientHeight - 100
) {
parentElement.classList.remove('sticky');
} else if (
window.innerWidth < MOBILE_BREAKPOINT &&
window.scrollY + window.innerHeight > findPos(courseAvail2)
) {
parentElement.classList.remove('sticky');
}
}
});
window.addEventListener('load', () => {
let startX;
let startY;
let dist;
const threshold = 150; // required min distance traveled to be considered swipe
const allowedTime = 500; // maximum time allowed to travel that distance
let elapsedTime;
let startTime;
function handleswipe(isupswipe) {
if (isupswipe) {
if (
parentElement
.querySelector('article')
.getAttribute('aria-expanded') === 'true'
) {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'false');
} else {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'true');
}
}
}
mobTouch.addEventListener('click', () => {
if (
parentElement.querySelector('article').getAttribute('aria-expanded') ===
'true'
) {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'false');
if (window.innerWidth > MOBILE_BREAKPOINT) {
mobTouch.innerHTML = 'Show Course Overview';
}
} else {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'true');
if (window.innerWidth > MOBILE_BREAKPOINT) {
mobTouch.innerHTML = 'Hide Course Overview';
}
}
});
mobSwipe.addEventListener('touchstart', e => {
const touchobj = e.changedTouches[0];
dist = 0;
startX = touchobj.pageX;
startY = touchobj.pageY;
startTime = new Date().getTime(); // record time when finger first makes contact with surface
// e.preventDefault();
});
mobSwipe.addEventListener('touchmove', () => {
// e.preventDefault(); // prevent scrolling when inside DIV
});
mobSwipe.addEventListener('touchend', e => {
const touchobj = e.changedTouches[0];
let swipedownBol = null;
let swipeupBol = null;
// check that elapsed time is within horizontal & vertical dist traveled >= threshold
dist = touchobj.pageY - startY; // get total dist traveled by finger while in contact
elapsedTime = new Date().getTime() - startTime; // get time elapsed
swipeupBol =
elapsedTime <= allowedTime &&
-dist >= threshold &&
Math.abs(touchobj.pageX - startX) <= 100;
swipedownBol =
elapsedTime <= allowedTime &&
dist >= threshold &&
Math.abs(touchobj.pageX - startX) <= 100;
// depending whether the tab is expanded or not, checking for up or down swipe
if (
parentElement.querySelector('article').getAttribute('aria-expanded') ===
'true'
) {
handleswipe(swipedownBol);
} else {
handleswipe(swipeupBol);
}
// e.preventDefault();
});
});
// touch to expand, specifically for the nudge only
mobTouch.addEventListener('touchstart', () => {
if (
parentElement.querySelector('article').getAttribute('aria-expanded') ===
'true' &&
window.innerWidth < MOBILE_BREAKPOINT
) {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'false');
} else if (window.innerWidth < MOBILE_BREAKPOINT) {
// eslint-disable-next-line no-param-reassign
parentElement
.querySelector('article')
.setAttribute('aria-expanded', 'true');
}
});
window.addEventListener('resize', () => {
if (defaultWidth !== window.innerWidth) {
parentElement.removeAttribute('style');
parentElement.classList.remove('sliding');
}
});
};
.course-overview {
max-width: 119.6rem;
box-shadow: 0 0.4rem 0.6rem 0 rgba(57, 48, 48, 0.15);
background-color: $white;
border-radius: 1em;
&_show-hide {
display: none;
@extend .efl-p-large;
}
.info-title-sticky {
display: none;
}
:last-child {
border-bottom: none;
}
header {
h2 {
font-family: $text-font-ef;
text-transform: uppercase;
font-size: 1.6rem;
line-height: 1.6rem;
letter-spacing: 0.14em;
max-width: 95.2rem;
text-align: left;
margin: 0;
}
background-color: $color-primary;
color: $white;
border-radius: 1rem 1rem 0 0;
padding: 1.6rem 3.2rem;
}
article {
padding: 1.6rem 3.2rem;
color: #5b6885;
display: flex;
flex-direction: column;
margin: 0 auto;
top: 0;
transition: top 0.3s ease-out, bottom 0.3s ease-out;
}
&__info {
border-bottom: 0.1rem solid $grey-light;
padding-top: 0.8rem;
padding-bottom: 1rem;
display: flex;
flex-direction: column;
align-items: stretch;
a:last-child {
margin-top: 0.8rem;
}
&:nth-child(3) {
padding-top: 0;
border-left: none;
}
.cta--efl {
color: $white;
max-width: none;
font-family: $text-font-ef;
font-size: 1.4rem;
line-height: 2.8rem;
letter-spacing: 0.01em;
min-height: 4.3rem;
&:hover {
color: $white;
}
}
.sticky-price {
display: none;
}
// P Small
> :first-child {
font-size: 1.6rem;
line-height: 2.4rem;
}
}
h3 {
font-family: $text-font-ef;
font-style: normal;
font-weight: 700;
font-size: 2rem;
line-height: 2.4rem;
color: $color-primary;
}
&__price {
font-size: 4.4rem !important;
line-height: 4.4rem !important;
}
p {
text-align: left;
@extend .efl-p-medium;
&:first-child {
@extend .efl-p-small;
}
}
@media screen and (max-width: $mq-medium) {
&.sticky {
article {
position: fixed;
bottom: 0;
background-color: $white;
left: 0;
right: 0;
filter: drop-shadow(0 0 6px rgba(91, 104, 133, 0.5));
border-radius: 1rem 1rem 0 0;
z-index: 99;
width: 100%;
top: initial;
transition: bottom 0.3s ease-out;
.course-overview__info {
transition: all 0.3s ease-out;
max-height: 0;
overflow: hidden;
padding: 0;
border-bottom: none;
&:last-child {
display: flex;
flex-direction: row;
gap: 2.4rem;
a {
flex-grow: 1;
margin-top: 0;
}
padding: 0;
max-height: initial;
.course-overview__price {
font-size: 2.4rem !important;
line-height: 2.6rem !important;
}
}
}
&[aria-expanded='true'] {
.course-overview__info {
max-height: initial;
overflow: visible;
border-bottom: 0.1rem solid $grey-light;
padding-top: 0.8rem;
padding-bottom: 1rem;
transition: all 0.3s ease-out;
&:last-child {
border-bottom: none;
padding-top: 3.1rem;
}
}
}
}
.course-overview_show-hide {
display: block;
text-align: center;
padding: 0.8rem;
margin-top: -1rem;
&::before {
content: '';
border-bottom: 0.3rem solid #5b6885;
border-radius: 10rem;
width: 4rem;
display: inline-block;
}
}
&.sliding {
article {
bottom: -7.5rem;
}
}
}
}
}
@media screen and (min-width: $mq-medium) {
.course-overview {
header {
padding-top: 0.9rem;
padding-bottom: 0.8rem;
h2 {
font-size: 1.7rem;
line-height: 2.7rem;
margin-left: 7.2rem;
}
}
article {
flex-direction: row;
justify-content: space-between;
padding: 3.4rem 3.2rem;
padding-left: 10.4rem;
padding-right: 10.4rem;
}
&__info {
border-bottom: none;
padding-left: 2.4rem;
padding-right: 2.4rem;
border-left: 0.1rem solid $grey-light;
padding-top: 0;
h3 {
font-size: 2.2rem;
line-height: 3rem;
}
&:nth-child(3) {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
.cta--efl {
min-width: 25.1rem;
}
}
&__sign-in {
margin: 1.5rem 0;
}
&.course-overview--cpd {
h3 {
letter-spacing: -0.01rem;
}
.course-overview__info {
.cta--efl {
max-width: 25.1rem;
min-width: none;
width: 100%;
}
}
article {
justify-content: start;
padding-left: 10.4rem;
h3 {
white-space: nowrap;
}
}
}
a:last-child {
border-bottom: none;
border-left: none;
}
&__price {
font-size: 4.2rem !important;
line-height: 4rem !important;
}
&.sticky {
article {
position: fixed;
display: grid;
grid-template-areas:
'infoA infoA infoB infoB infoC infoC'
'infoCta infoCta 1fr 1fr infoExpand infoExpand';
grid-template-columns: repeat(6, minmax(100px, 500px));
align-items: center;
padding: 2.35rem calc(50vw - 54.8rem);
bottom: 0;
background-color: $white;
left: 0;
right: 0;
filter: drop-shadow(0 0 6px rgba(91, 104, 133, 0.5));
z-index: 99;
width: 100%;
top: initial;
transition: bottom 0.3s ease-out;
.course-overview_show-hide {
display: block;
white-space: nowrap;
padding-left: 0;
&::before {
content: '';
background-image: url('./assets/images/red-arrow.svg');
background-repeat: no-repeat;
background-position: center;
width: 1.7rem;
height: 1.05rem;
margin-right: 1.6rem;
display: inline-block;
transform: rotate(180deg);
}
}
.course-overview__info {
max-height: 0;
overflow: hidden;
border-bottom: none;
&:last-child {
display: flex;
flex-direction: row;
gap: 2.4rem;
a {
flex-grow: 1;
margin-top: 0;
}
padding: 0 0 0 3.2rem;
max-height: initial;
.course-overview__price {
font-size: 2.4rem !important;
line-height: 2.6rem !important;
}
}
}
&[aria-expanded='false'] {
.course-overview__info {
padding: 0;
&:last-child {
padding: 0 0 0 3.2rem;
overflow: visible;
}
}
}
&[aria-expanded='true'] {
row-gap: 5.2rem;
/* stylelint-disable no-descending-specificity */
.course-overview__info {
margin-bottom: auto;
max-height: initial;
min-height: -webkit-fill-available;
overflow: visible;
border-bottom: none;
padding-top: 0.8rem;
padding-bottom: 1rem;
transition: max-height 0.3s ease-out;
&:last-child {
border-bottom: none;
padding: 0 0 0 3.2rem;
overflow: visible;
}
}
.course-overview_show-hide::before {
transform: rotate(0deg);
}
}
& > * {
grid-area: info;
grid-column: span 2;
grid-row: 1;
padding-left: 4rem;
}
& > :last-child {
grid-area: infoCta;
grid-column: span 2;
border-left: none;
}
& > :first-child {
grid-area: infoExpand;
grid-column: 5 / span 2;
cursor: pointer;
}
}
&.sliding {
article {
bottom: -7.5rem;
}
}
}
}
}
@media screen and (min-width: 1016px) and (max-width: 1266px) {
.course-overview {
margin: 0 3rem;
}
}
@media screen and (min-width: $mq-medium) and (max-width: 1196px) {
.course-overview {
.header {
padding: 0;
}
&--cpd.course-overview {
article {
padding-left: 2rem;
h3 {
white-space: normal;
}
}
}
p {
white-space: normal;
}
}
}
<section class="course-overview" data-behavior="course-overview">
<header>
<h2>Course Overview</h2>
</header>
<article data-brand="mens" class="course-overview__inner">
<a class="course-overview_show-hide"></a>
<div class="info-title-sticky">Introduction to Coaching Football</div>
<div class="course-overview__info">
<div>
<p>Course type:</p>
{{#unless isOnline}}
<h3>{{venue}}</h3>
{{else}}
<h3>Online Only</h3>
{{/unless}}
</div>
<div class="sticky-price">
<h3>£{{price}}</h3>
</div>
</div>
<div class="course-overview__info">
<p>Duration:</p>
<h3>{{duration}}</h3>
<p>
{{#each content}}{{this}}<br>{{/each}}
</p>
</div>
<div class="course-overview__info">
<p>Leading To:</p>
<h3>{{leadingTo.name}}</h3>
<p>{{leadingTo.category}}</p>
</div>
<div class="course-overview__info">
<div>
<p>Price:</p>
<h3 class="course-overview__price">£{{price}}</h3>
</div>
{{render '@cta' course-overview-cta merge="true" }}
</div>
</article>
</section>