Scrolling to element in "Sticky" element

This is an example of trying to scroll to an element that is located in a sticky element.

by dswitzer

HTML

<div class="layout-header" id="layout-header">
	<h1>
		Scrolling to element in "Sticky" element
	</h1>
</div>

<div class="layout-main-container">
	<div class="layout-content">
		<p>
			This is an example to show of an issue when trying to scroll to an element that is within a "sticky"
			element. The sidebar element to the right, contains an input at the top of the sidebar. What we are
			trying to accomplish is to correctly scroll the element into view when the button in the footer
			below is clicked.
		</p>
		<p>
			If you try scrolling the page down, just far enough for the input element to get hidden under the fixed
			header, you will see that the link is correctly scrolled into view. It will work correctly, as
			long as the sidebar has not gotten to its "stuck" position.
		</p>
		<p>
			However, if you scroll to the very bottom of the page, the input element is not scrolled into view.
		</p>
		<p>
			The reason this happens, is because when the code attempts to scroll the element into view it 
			will only adjust the scrolling based on how far above the current viewport the element
			we bring into focus is. The problem is, we actually need to scroll far enough up in the document
			that the sidebar becomes "unstuck" before the calculated offset will work properly. This
			is why the scrolling works correctly when the sidebar is not yet "stuck" and only
			breaks once the sidebar becomes stuck.
		</p>
		<p>
			To resolve the issue, I'm trying to get the findStickyParentOffset() JavaScript function
			to be able to detect if the "layout-sidebar" element is in a "stuck" position and then
			how much further the window would need to scroll in order to get the element for the element
			to no longer be in "stuck" state. 
		</p>
		<p>
			To do this what we need to be able to do is figure out what the "scrollTop" value would 
			in order for the element to leave the "stuck" state.
		</p>
		<!-- just some fake content to fill in the space for scrolling...

CSS

body {
	/* add padding for the fixed header/footer */
	padding: 80px 0;
}

.layout-header {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	
	background: #cae2f2;
	height: 80px;
	text-align: center;
	z-index: 2;
}

.layout-footer {
	position: fixed;
	bottom: 0;
	left: 0;
	right: 0;
	
	background: #cae2f2;
	height: 80px;
	text-align: center;
	z-index: 2;
}

.layout-main-container {
	display: flex;
	flex-flow: row nowrap;
	align-items: flex-start;
}

.layout-content {
	order: 1;
	flex: 1 1 auto;
	width: calc(100% - 255px);
}

.layout-sidebar {
	position: sticky;
	top: calc((100vh - 100px)*-1);
	
	order: 2;
	flex: 0 0 auto;
	width: 220px;
	margin: 0 0 0 2.5em;
	padding: 1.75em;
	background-color: #ebebeb;
	
	z-index: 1;
}

.content-filler {
	opacity: 0.15;
}

.content-copyright {
	text-align: center;
	font-size: 0.85em;
}

JavaScript

UIUtils = {};

UIUtils.focusElement = function (id){
	var el = document.getElementById(id);
	setTimeout(function (){
		el.focus();
	}, 50);
}

/*
 * This listens for focusable items that have received focus from a non-pointer
 * event and makes sure the element is visible in the viewport. This function
 * was written to fix issues where fixed/sticky elements can end up keeping
 * elements hidden from the user's view because the element is not scrolled
 * into the viewport correctly.
 */
UIUtils.applyAutoscrollFocusBehavior = function(options){
	options = jQuery.extend({
		// defaults
		  selector: "a, input, textarea, select, button"
		, header: null // can either an element id or an HTML node
		, footer: null // can either an element id or an HTML node
		, scrollOffset: 50
		, scrollSpeed: 150
	}, options);

	var isFixedPosition = function (node){
		// determine the CSS property
		var position = getComputedStyle(node, null).getPropertyValue("position");

		// both positions of "fixed" and "sticky" can have fixed positions in the viewport
		return ((position === "fixed") || (position === "sticky"));
	};

	var findStickyParent = function (node){
		do {
			if( getComputedStyle(node, null).getPropertyValue("position") == "sticky" ){
				return node;
			}
		} while ( node = node.offsetParent );

		return null;
	}

	var hasStickyParent = function (node){
		return findStickyParent(node) ? true : false;
	}

	// we never want to apply our focus behavior when the user uses a pointer to put focus on an element
	var preventAutoscrollBehavior = false;
	// we monitor for pointer events, so we can make sure to cancel our focus correction behavior
	jQuery(document).on("mousedown mouseup touchstart touchend", options.selector, function (event){
		// we need to track if the pointer was used and if so, we skip the scrolling behavior
		preventAutoscrollBehavior = ((event.type == "mousedown") || (event.type == "touchstart"));
	});

	// in some browser (e.g. Firefox 67.0.1),...