JSFiddle - React, Tailwind, and code Playground
by Graham Dixon
HTML
<div class="editor">
<span class="line"><span class="line-content">These <span class="line-content">test </span></span><h1 style="font-size:20px;"><u><span class="line-content">lines</span></u></h1><span class="line-content"> can be fake selected</span></span>
<span class="line"><span class="line-content">These lines can be fake selected</span></span>
<span class="line"><span class="line-content">These lines can be fake selected</span></span>
<span class="line"><span class="line-content">These lines can be fake selected</span></span>
<span class="line"><table>
<tr><td><span class="line-content">These lines can be fake selected</span></td><td><span class="line-content">These lines can be fake selected</span></td></tr>
</table></span>
</div>
CSS
.selected-content:not(.line), .line.selected-content .line-content {
background: #ACCEF7
}
.line {
user-select: none;
display: inline-block;
cursor: text;
width: 100%;
}
.fake-selection {
position: absolute;
color: transparent;
z-index: -1;
}
.line-content {
z-index: 1;
}
JavaScript
// climbing to parent with matching selector
const closest = function(elem, selector) {
// Element.matches() polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
// Get the closest matching element
for (; elem && elem !== document; elem = elem.parentNode) {
if (elem.matches(selector)) return elem;
}
// undiscovered
return null;
};
// range to get points from the dom
const range = function(e) {
// diiferent environments use different methods to get to the same values
var range, textNode, offset;
// switch to get the correct range from point method
if (document.caretPositionFromPoint) {
// standard uses caretPositionFromPoint
range = document.caretPositionFromPoint(e.pageX, e.pageY);
textNode = rage && range.offsetNode;
offset = range && range.offset;
} else if (document.caretRangeFromPoint) {
// webkit uses caretRangeFromPoint
range = document.caretRangeFromPoint(e.pageX, e.pageY);
textNode = range && range.startContainer;
offset = range && range.startOffset;
}
// ensure we have a range to return
if (range) {
// return the response object
return {
range: range,
textNode: textNode,
offset: offset,
x: e.pageX,
y: e.pageY
}
}
};
// set up selection - from outside create a new selection instance per...