Skip to content

Instantly share code, notes, and snippets.

@alisey
Created September 18, 2012 17:45
Show Gist options
  • Save alisey/3744577 to your computer and use it in GitHub Desktop.
Save alisey/3744577 to your computer and use it in GitHub Desktop.
Scroll selection into view
// Scroll to selection focus, but only if it's out of view. Align selection
// focus with the top or bottom edge of its scroll-container. Return true
// on success.
// * there might be several nested scroll-containers, including window
// * must not try to scroll overflow:hidden and overflow:visible elements
// * no scrolling should happen if selection focus is visible
// * selection is not necessarily collapsed
// * range.getBoundingClientRect doesn't work for collapsed ranges
// * Opera reports incorrect startOffset and endOffset for collapsed ranges
// outside of text nodes (e.g. between 2 <br> elements), range.insertNode
// doesn't work correctly in that case too.
// * horizontal scroll is ignored
function scrollSelectionIntoView() {
var sel, range, marker, container, rect, top, bottom,
containerTop, containerBottom, scroll;
// IE8, IE9
if (document.selection) {
// scrolls only if needed
document.selection.createRange().scrollIntoView();
return true;
}
sel = window.getSelection();
if (!sel.focusNode) {
return false;
}
// collapse to focus point
range = document.createRange();
range.setStart(sel.focusNode, sel.focusOffset);
if ($.browser.opera && range.startContainer.nodeType == 1) {
return false;
}
// zero-width space
marker = $('<span style="visibility: hidden">\u200B</span>')[0];
range.insertNode(marker);
container = marker;
if (marker.scrollIntoViewIfNeeded) {
marker.scrollIntoViewIfNeeded(true); // WebKit
} else while (container != window) {
container = container.parentNode || window;
if (container === window) {
containerTop = 0;
containerBottom = $(container).height();
} else if (
container.scrollHeight > container.clientHeight &&
/scroll|auto/.test($(container).css('overflow'))
) {
rect = container.getBoundingClientRect();
containerTop = rect.top;
containerBottom = rect.bottom;
} else {
continue;
}
rect = marker.getBoundingClientRect();
top = rect.top;
bottom = rect.bottom;
scroll = $(container).scrollTop();
if (top < containerTop) {
$(container).scrollTop(scroll + top - containerTop - 20);
} else if (bottom > containerBottom) {
$(container).scrollTop(scroll + bottom - containerBottom + 20);
}
}
container = marker.parentNode;
container.removeChild(marker);
container.normalize();
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment