Handle overlaps in scroll areas on focus events

When focus changes in scroll areas, check the
current focused element isn't overlapped by sticky
elements in the area.

If there are overlaps, mimic what browsers do if
focus moves outside the viewport and scroll to
move the focused element into view.
This commit is contained in:
Tom Byers
2019-03-11 16:26:11 +00:00
parent b13bc158ac
commit b8c5ab5e38

View File

@@ -16,6 +16,7 @@
this._els = [el];
this.edge = edge;
this.node = scrollArea;
this.setEvents();
};
ScrollArea.prototype.addEl = function (el) {
this._els.push(el);
@@ -26,6 +27,24 @@
ScrollArea.prototype.updateEls = function (usedEls) {
this._els = usedEls;
};
ScrollArea.prototype.setEvents = function () {
this.node.addEventListener('focus', this.focusHandler.bind(this), true);
};
ScrollArea.prototype.removeEvents = function () {
this.node.removeEventListener('focus', this.focusHandler.bind(this));
};
ScrollArea.prototype.focusHandler = function (e) {
var $focusedElement = $(document.activeElement);
var endOfFurthestEl = focusOverlap.endOfFurthestEl(this._els, this.edge);
var overlap = focusOverlap.getOverlap($focusedElement, this.edge, endOfFurthestEl);
if (overlap > 0) {
$(window).scrollTop($(window).scrollTop() + overlap);
}
};
ScrollArea.prototype.destroy = function () {
this.removeEvents();
};
// Object collecting together methods for interacting with scrollareas
var scrollAreas = {
@@ -95,6 +114,44 @@
}
};
// Object collecting together methods for stopping sticky overlapping focused elements
var focusOverlap = {
getOverlap: function ($focusedElement, edge, endOfFurthestEl) {
var topOfFocusedElement = $focusedElement.offset().top;
if (!endOfFurthestEl) { return 0; }
if (edge === 'top') {
return endOfFurthestEl - topOfFocusedElement;
} else {
return (topOfFocusedElement + $focusedElement.outerHeight()) - endOfFurthestEl;
}
},
endOfFurthestEl: function (els, edge) {
var stuckEls = $.grep(els, function (el) { return el.isStuck(); });
var edgeOfEl;
var offsets;
if (edge === 'bottom') {
edgeOfEl = function (el) {
return el.$fixedEl.offset().top;
};
} else {
edgeOfEl = function (el) {
return el.$fixedEl.offset().top + el.height;
};
}
if (!stuckEls.length) { return false; }
offsets = $.map(stuckEls, function (el) { return edgeOfEl(el); });
return offsets.reduce(function (accumulator, offset) {
return (accumulator < offset) ? offset: accumulator;
});
}
};
// 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 = {