Reputation: 99
I have a text block that is of a fixed width, and I'm trying to get each line of text's boundingClientRect. I'm wondering if the Selection api has a method for doing so that I'm just missing. Is there any simple way to get each line of a selected text?
Currently, the only solution I can think of is to create spans around each character in the selection and then join the characters if they have the same y coordinate in their bounding client rect. This is very inefficient, so I'm curious if there's a more simple method.
Below is an image representation of what I'm trying to find. The selection api only gives a rectangle around the box as a whole, including unselected text. I'm trying to find rectangles around each selection line.
The selection will always be text. So no need to worry about images. However, they may be in different elements, but they will be the same tag type (no links for example or em/strong tags).
Returns the selected block of text
const selection = window.getSelection().getRangeAt(0);
markjs has a solution for highlighting that wraps everything in a single tag.
<mark><p>hello there</p></mark>
My plan was to use a similar algorithm to get the inner html and go through each character and wrap it in a new tag with a classname that could then be found and then calculate the bounding box. This is straightforward to write, but it seems more computationally heavy than I'd like.
Is there any way to simply get each line's bounding box?
Upvotes: 2
Views: 533
Reputation: 10886
You can use getClientRects
instead:
var selectionDivs = [];
document.addEventListener('selectionchange', function () {
var range = window.getSelection().getRangeAt(0);
var rects = range.getClientRects();
selectionDivs.forEach(function (div) {
div.remove();
});
selectionDivs = [];
for (var i = 0; i < rects.length; i++) {
var div = document.createElement('div');
div.className = 'rect';
div.style.left = (window.scrollX + rects[i].left) + 'px';
div.style.top = (window.scrollY + rects[i].top) + 'px';
div.style.width = rects[i].width + 'px';
div.style.height = rects[i].height + 'px';
document.body.appendChild(div);
selectionDivs.push(div);
}
});
.rect {
position: absolute;
pointer-events: none;
border: 1px solid black;
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nunc sed velit dignissim sodales ut eu. Imperdiet sed euismod nisi porta lorem mollis. Nisl suscipit adipiscing bibendum est. Elit eget gravida cum sociis natoque penatibus et. Mauris in aliquam sem fringilla. In arcu cursus euismod quis viverra. Imperdiet sed euismod nisi porta lorem mollis. Eget gravida cum sociis natoque penatibus et. Sed libero enim sed faucibus turpis in eu mi bibendum. Varius duis at consectetur lorem donec massa. Facilisi nullam vehicula ipsum a arcu. Libero justo laoreet sit amet cursus sit. At in tellus integer feugiat scelerisque varius morbi. Est ullamcorper eget nulla facilisi etiam. Semper eget duis at tellus at urna condimentum mattis.
Upvotes: 3