Reputation: 3297
I am displaying some HTML in a web view in my app (it's a WKWebView
in an iOS app, but that part is irrelevant as the same HTML/JavaScript is used on multiple platforms). External to the web view is a way the user can say "next page". In response to this, I want to scroll the document by one screen-height. That's pretty simple and I can easily do that and it works great.
The problem is that if the last visible line is split between one "page" and the next, you see the top half of the line on one page and the bottom half on the next page. This is difficult to read. So ideally, I'd like to iterate through the HTML document by rendered line and find the last line that is fully visible, then scroll so that the line after that is at the top of the screen. Naturally this assumes no tables, images, floating elements etc. to complicate the definition of what constitutes a "line", but I can deal with that if I can solve the simpler case first.
I can iterate over paragraphs in the DOM and finding bounding rectangles for those, but don't see an obvious way to break it down any further than that. I can imagine a solution in which I wrap each word in my HTML with a <span>
, then iterate over those looking at position and height to determine where the bottom of the screen is, but that sounds pretty inefficient.
I have control over the CSS and JavaScript in my displayed document (all documents use the same CSS and JavaScript, which is added at runtime) and can make modifications to the HTML at runtime if I have to, but performance is obviously an issue so I don't want to do a lot of monkeying around with the HTML I'm displaying if I don't have to.
I strongly suspect this is impossible without adding <span>
tags as described above. Any other suggestions?
Upvotes: 0
Views: 292
Reputation: 350272
I would go through all paragraphs (mainly p
, but other tags could be considered as well), get their top offset, and find the last one that still has its top offset in the viewable range. This should be the top paragraph after performing the controlled "page down" operation.
Here is some code relying on jQuery
to return the offset of that element:
function offsetForNextPage() {
var bottom = $(window).scrollTop() + $(window).height();
var result = $(window).scrollTop() + $(window).height() * 0.75; // minimum scroll amount
$('p, span, div').each(function(idx) {
if ($(this).offset().top >= bottom) {
return false;
}
result = Math.max($(this).offset().top, result);
});
return result;
}
You can of course add the tags you want to include in this calculation. Note that if no suitable element is found, or it would result into an offset that is considered too small, a minimum offset is returned instead. The above function has set that minimum to the current offset plus 75% of the viewport height.
Here is how it would look if the space key would trigger a page-down:
$(document).keydown(function (e) {
if (e.which === 32) {
e.preventDefault();
$('html, body').animate({
scrollTop: offsetForNextPage()
}, 1000);
}
});
Here is a fiddle.
NB: for $(window).height()
to give correct values, you must have a valid document type declaration at the start of the html
page, like:
<!doctype html>
Upvotes: 4