Add box shadow by JS

When mode === 'dialog', sticky elements are
stacked so we need to apply the box shadow to the
top (when sticking to the bottom edge) and bottom
(when sticking to the top edge) elements in the
stack.

From what I can see, we need the version of
`nth-child` that supports targeting by selector.
As of this date, support for this is only in
Safari:

https://caniuse.com/#feat=css-nth-child-of

Until we can use this version of `nth-child`, we
need to use JS to apply the styles.
This commit is contained in:
Tom Byers
2019-01-18 11:44:59 +00:00
parent 4e845c3125
commit a9b7a0d887
2 changed files with 80 additions and 27 deletions

View File

@@ -5,6 +5,41 @@
var GOVUK = global.GOVUK || {};
var _mode = 'default';
// Object collecting together methods for dealing with marking the edge of a sticky, or group of
// sticky elements (as seen in dialog mode)
var oppositeEdge = {
_classes: {
'top': 'content-fixed__top',
'bottom': 'content-fixed__bottom'
},
_getClassForEdge: function (edge) {
return this._classes[edge];
},
mark: function (sticky) {
var edgeClass = this._getClassForEdge(sticky.edge);
var els;
if (_mode === 'dialog') {
els = [dialog.getElementAtOppositeEnd(sticky)];
} else {
els = sticky._els;
}
els = $.grep(els, function (el) { return el.isStuck(); });
$.each(els, function (i, el) {
el.$fixedEl.addClass(edgeClass);
});
},
unmark: function (sticky) {
var edgeClass = this._getClassForEdge(sticky.edge);
$.each(sticky._els, function (i, el) {
el.$fixedEl.removeClass(edgeClass);
});
}
};
// Constructor for objects holding data for each element to have sticky behaviour
var StickyElement = function ($el, sticky) {
this._sticky = sticky;
@@ -23,19 +58,23 @@
StickyElement.prototype.appliedClass = function () {
return this._appliedClass;
};
StickyElement.prototype.removeStickyClasses = function () {
this.$fixedEl.removeClass([this._initialFixedClass, this._fixedClass].join(' '));
StickyElement.prototype.removeStickyClasses = function (sticky) {
this.$fixedEl.removeClass([
this._initialFixedClass,
this._fixedClass
].join(' '));
};
StickyElement.prototype.isStuck = function () {
return this._appliedClass !== null;
};
StickyElement.prototype.stick = function () {
StickyElement.prototype.stick = function (sticky) {
this._appliedClass = this.stickyClass();
this.$fixedEl.addClass(this._appliedClass);
this._hasBeenCalled = true;
};
StickyElement.prototype.release = function () {
StickyElement.prototype.release = function (sticky) {
this._appliedClass = null;
this.removeStickyClasses(sticky);
this._hasBeenCalled = true;
};
// When a sticky element is moved into the 'stuck' state, a shim is inserted into the
@@ -101,16 +140,6 @@
_elsThatCanBeStuck: function (els) {
return $.grep(els, function (el) { return el.canBeStuck(); });
},
hasOppositeEdge: function (el, sticky) {
var els = this._elsThatCanBeStuck(sticky._els);
var idx;
if (els.length < 2) { return true; }
idx = els.indexOf(el);
return (sticky.edge === 'top') ? idx === (els.length - 1) : idx === 0;
},
getOffsetFromEdge: function (el, sticky) {
var els = this._elsThatCanBeStuck(sticky._els).slice();
var elIdx;
@@ -157,11 +186,8 @@
var self = this;
var els = sticky._els.slice();
var height = sticky.getWindowDimensions().height;
var elsThatCanBeStuck = function () {
return $.grep(els, function (el) { return el.canBeStuck(); });
};
var totalStickyHeight = function () {
return self._getTotalHeight(elsThatCanBeStuck());
return self._getTotalHeight(self._elsThatCanBeStuck(els));
};
var dialogFitsHeight = function () {
return totalStickyHeight() <= height;
@@ -176,8 +202,8 @@
// reset elements
$.each(els, function (i, el) { el.canBeStuck(true); });
while (elsThatCanBeStuck().length && !dialogFitsHeight()) {
var currentEl = elsThatCanBeStuck()[0];
while (self._elsThatCanBeStuck(els).length && !dialogFitsHeight()) {
var currentEl = self._elsThatCanBeStuck(els)[0];
sticky.reset(currentEl);
currentEl.canBeStuck(false);
@@ -187,10 +213,21 @@
return this._getTotalHeight(els);
},
getInPageEdgePosition: function (sticky) {
var idx = (sticky.edge === 'top') ? 0 : sticky._els.length - 1;
getElementAtStickyEdge: function (sticky) {
var els = this._elsThatCanBeStuck(sticky._els);
var idx = (sticky.edge === 'top') ? 0 : els.length - 1;
return sticky._els[idx].inPageEdgePosition;
return els[idx];
},
// get element at the end opposite the sticky edge
getElementAtOppositeEnd: function (sticky) {
var els = this._elsThatCanBeStuck(sticky._els);
var idx = (sticky.edge === 'top') ? els.length - 1 : 0;
return els[idx];
},
getInPageEdgePosition: function (sticky) {
return this.getElementAtStickyEdge(sticky).inPageEdgePosition;
},
getHeight: function (els) {
return this._getTotalHeight(this._elsThatCanBeStuck(els));
@@ -272,12 +309,18 @@
}
};
// clean up any existing styles marking the edges of sticky elements
oppositeEdge.unmark(self);
$.each(self._els, function (i, el) {
if (el.canBeStuck()) {
_setElementPosition(el);
}
});
// add styles to mark the edge of sticky elements opposite to that stuck to the window
oppositeEdge.mark(self);
if (self._initialPositionsSet === false) { self._initialPositionsSet = true; }
};
// Store all the dimensions for a sticky element to limit DOM queries
@@ -489,13 +532,13 @@
if (el.isStuck()) {
var $el = el.$fixedEl;
el.removeStickyClasses();
el.removeStickyClasses(this);
$el.css('width', '');
if (_mode === 'dialog') {
dialog.releaseEl(el, this);
}
el.removeShim();
el.release();
el.release(this);
}
};
@@ -560,7 +603,7 @@
'width': $el.width() + 'px',
'top': offset + 'px'
});
el.stick();
el.stick(this);
}
};
stickAtTop.stop = function (el) {
@@ -641,7 +684,7 @@
'width': $el.width() + 'px',
'bottom': offset + 'px'
});
el.stick();
el.stick(this);
}
};
stickAtBottom.stop = function (el) {

View File

@@ -86,6 +86,11 @@
top: 0;
margin-top: 0;
}
.js-stick-at-top-when-scrolling.content-fixed__top {
border-bottom: 1px solid $border-colour;
box-shadow: 0 2px 0 0 rgba($border-colour, 0.2);
@@ -102,6 +107,11 @@
top: auto; // cancel `top: 0;` inherited from govuk-template
bottom: 0;
}
.js-stick-at-bottom-when-scrolling.content-fixed__bottom {
border-top: 1px solid $border-colour;
box-shadow: 0 -2px 0 0 rgba($border-colour, 0.2);