PrismJS is a syntax highlighter for source code in Web pages. It has a line-highlight plugin that highlights certain lines in the source code. In particular, that plugin supports determining what lines to highlight from the hash (e.g., #play.5-6
says to highlight lines 5 through 6 of the <pre>
element with an id
of play
).
For that hash, the plugin uses scrollIntoView()
to ensure that the highlighted lines are visible:
document.querySelector('.temporary.line-highlight').scrollIntoView();
(here line-highlight
is added as a class to the lines)
However, scrollIntoView()
puts the highlighted lines at the top. In my use case, it would be better to have the highlighted lines be vertically centered, assuming that the highlighted range is shorter than the available space.
What would I replace that above line with, in order to have those highlighted lines be vertically centered?
FWIW:
While I'm OK with CSS/JS, I'm not an expert
If it matters, my use case is to show this code in a WebView
widget in an Android app
Here is a function that will center the highlighted lines when scrolling (assuming that the scrollbar is on the pre
element):
function scrollToLines (pre) {
var lines = document.querySelector('.temporary.line-highlight'),
linesHeight = lines.offsetHeight,
preHeight = pre.offsetHeight;
lines.scrollIntoView();
if (preHeight > linesHeight && pre.scrollTop < (pre.scrollHeight - preHeight)) {
pre.scrollTop = pre.scrollTop - (preHeight / 2) + (linesHeight / 2);
}
}
Just call this function in the appyHash
function after the highlightLines
function is called:
function applyHash() {
// ...
highlightLines(pre, range, 'temporary ');
scrollToLines(pre);
}
Explanation:
Just like before, the scrollIntoView()
method is invoked and the pre
element is scrolled to the top of the first highlighted line.
If both of these conditions are true...
preHeight > linesHeight
- The height of the highlighted range is less than the height of the pre
element and:pre.scrollTop < (pre.scrollHeight - preHeight)
- The pre
element wasn't scrolled to the bottom (i.e., the current scroll position is less than the available scrollable height minus the height of the pre
element)....then subtract half of the pre
element's height from the current scroll position and add half of the highlighted line's height. In doing so, the highlighted lines will be centered vertically if the range's height doesn't exceed the height of the pre
element.
Basic test cases based on the live snippet that I was using when writing the code:
#scroll.40-44
, #scroll.107
, #scroll.40-62
, #scroll.4-6
, #scroll.140-144
As you pointed out in the comments, this wasn't working for you because the scrollbar was actually on the body
element (rather than the pre
element like above).
To resolve this, change the logic so that the calculations are relative to the body
element and window:
function scrollToLines () {
var lines = document.querySelector('.temporary.line-highlight'),
linesHeight = lines.offsetHeight,
body = document.body,
windowHeight = window.innerHeight;
lines.scrollIntoView();
if (windowHeight > linesHeight && body.scrollTop < (body.scrollHeight - windowHeight)) {
body.scrollTop = body.scrollTop - (windowHeight / 2) + (linesHeight / 2);
}
}
Here are some basic test cases demonstrating that it works when the highlighted range is at the top/bottom or if the height of the highlighted range exceeds the height of the window:
#src.42-58
, #src.2-9
, #src.34-40
, #src.15-80
, #src.80-88
Since you may not know whether the scrollbar will be on the pre
element or the body
, you could check if the pre
element is scrollable first and then fall back to making the calculations relative to the body
element if it isn't:
function scrollToLines (pre) {
var lines = document.querySelector('.temporary.line-highlight'),
linesHeight = lines.offsetHeight,
pre = pre.scrollHeight > pre.clientHeight ? pre : document.body,
preHeight = pre === document.body ? window.innerHeight : pre.offsetHeight;
lines.scrollIntoView();
if (preHeight > linesHeight && pre.scrollTop < (pre.scrollHeight - preHeight)) {
pre.scrollTop = pre.scrollTop - (preHeight / 2) + (linesHeight / 2);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With