Skip to content

Instantly share code, notes, and snippets.

@unarist
Last active April 16, 2016 15:22
Show Gist options
  • Save unarist/2966e034b3f2995e97cd58d479a1fff1 to your computer and use it in GitHub Desktop.
Save unarist/2966e034b3f2995e97cd58d479a1fff1 to your computer and use it in GitHub Desktop.
マウスカーソルが乗っている文字をハイライト
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.mask-elem { position: absolute; opacity: 0.1; z-index: -1; background: blue; }
.mask-node { position: absolute; opacity: 0.3; z-index: -1; background: green; }
.mask-char { position: absolute; opacity: 0.5; z-index: -1; background: red; }
</style>
<body>
<p>
Lorem ipsum dolor sit amet, <b>consectetur</b> adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <i>Ut</i> enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p>
吾輩は猫である。名前はまだ無い。<u>どこで生れたか</u>とんと見当がつかぬ。<br/>
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
</p>
<div class="mask-elem"></div>
<div class="mask-node"></div>
<div class="mask-char"></div>
<script>
document.addEventListener("mousemove", function(e){
hideMask('mask-elem');
hideMask('mask-node');
hideMask('mask-char');
var elemRange = getElementRange(e.target);
showMask('mask-elem', [elemRange.getBoundingClientRect()]);
var nodeRange = findNodeRange(elemRange, e.clientX, e.clientY);
showMask('mask-node', nodeRange.getClientRects());
if (nodeRange.startContainer.nodeType === Node.TEXT_NODE) {
var charRange = findCharRange(nodeRange, e.clientX, e.clientY);
showMask('mask-char', charRange.getClientRects());
}
}, true);
function getElementRange(element) {
var range = document.createRange();
range.selectNodeContents(element);
return range;
}
function findNodeRange(elemRange, clientX, clientY) {
var range = elemRange.cloneRange();
// childNodesから探す
for (var node of elemRange.startContainer.childNodes) {
if (node.nodeType !== Node.TEXT_NODE) continue;
range.selectNodeContents(node);
if (pointInRange(range, clientX, clientY))
break;
}
if (!pointInRange(range, clientX, clientY))
range.collapse();
return range;
}
function findCharRange(nodeRange, clientX, clientY) {
range = nodeRange.cloneRange();
var lc = 0;
while (range.endOffset - range.startOffset > 1) {
var s = range.startOffset,
e = range.endOffset,
mid = s + (e - s) / 2;
range.setEnd(range.startContainer, mid);
if (!pointInRange(range, clientX, clientY)) {
range.setStart(range.startContainer, mid);
range.setEnd(range.startContainer, e);
}
// 16回もやれば2^16=65536文字までいけるはずなので
// 超えていたら中止
if (++lc >= 16) break;
}
return range;
}
function pointInRange(range, x, y) {
for (var rect of range.getClientRects())
if (rect.left <= x && x <= rect.right &&
rect.top <= y && y <= rect.bottom)
return true;
return false;
}
function hideMask(className) {
var masks = document.getElementsByClassName(className);
for (var mask of masks)
mask.style.display = "none";
}
function showMask(className, clientRects) {
var masks = document.getElementsByClassName(className);
var maskNum = 0;
for (var rect of clientRects) {
var mask = masks[maskNum++];
if (mask === undefined) {
mask = masks[0].cloneNode();
masks[0].parentElement.appendChild(mask);
}
mask.style.display = "block";
mask.style.left = (window.scrollX + rect.left) + "px";
mask.style.top = (window.scrollY + rect.top) + "px";
mask.style.width = rect.width + "px";
mask.style.height = rect.height + "px";
}
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment