Reputation: 740
I'm trying to write a JavaScript which will highlight an element in the DOM when the user hovers over it. This is supposed to be a cross-browser external plug-in. Ideally, i'm trying to mimic the behaviour of the browser inspector tools.
I can't say I have no success, but I am stuck with two options, both with its own pros and cons.
Approach 1
I handle the mouseover
event and simply add a border to the target element. When I hover on another element, I just reset the existing highlighted elements. The code for the same is below:
function addHighlight(target) {
target.classList.add('highlighted');
}
function removeHighlight(target) {
target.classList.remove('highlighted');
}
window.addEventListener('mouseover',function(e) {
addHighlight(e.target);
});
window.addEventListener('mouseout',function(e) {
removeHighlight(e.target);
});
Pros with this Approach
It works absolutely fine.
Cons with this approach
As I'm adding a border to existing DOM elements, it sort of re-arranges the elements on the page, and you can observe a slight shuffling effect of the elements. Doesn't look great.
Approach 2
I wanted the highlighting to be seamless. That is, preserving the look of the page and simply overlaying a highlight mask on top of the element.
TO do this, in the mouseover
event, I dynamically created a mask element, whose position
is set to absolute
and its co-ordinates set to the exact co-ordinates of the target element. Below is my code:
window.addEventListener('mouseover',function(e) {
applyMask(e.target);
});
function applyMask(target) {
if(document.getElementsByClassName('highlight-wrap').length > 0) {
resizeMask(target);
}else{
createMask(target);
}
}
function resizeMask(target) {
var rect = target.getBoundingClientRect();
var hObj = document.getElementsByClassName('highlight-wrap')[0];
hObj.style.top=rect.top+"px";
hObj.style.width=rect.width+"px";
hObj.style.height=rect.height+"px";
hObj.style.left=rect.left+"px";
// hObj.style.WebkitTransition='top 0.2s';
}
function createMask(target) {
var rect = target.getBoundingClientRect();
var hObj = document.createElement("div");
hObj.className = 'highlight-wrap';
hObj.style.position='absolute';
hObj.style.top=rect.top+"px";
hObj.style.width=rect.width+"px";
hObj.style.height=rect.height+"px";
hObj.style.left=rect.left+"px";
hObj.style.backgroundColor = '#205081';
hObj.style.opacity='0.5';
hObj.style.cursor='default';
//hObj.style.WebkitTransition='top 0.2s';
document.body.appendChild(hObj);
}
function clearMasks() {
var hwrappersLength = document.getElementsByClassName("highlight-wrap").length;
var hwrappers = document.getElementsByClassName("highlight-wrap");
if(hwrappersLength > 0) {
for(var i=0; i<hwrappersLength; i++) {
console.log("Removing existing wrap");
hwrappers[i].remove();
}
}
}
Pros with this approach I feel this is more elegant, and doesn't disturb the page, just overlays a mask on top of elements.
Cons
When the user hovers over the top most container (div
), it creates a mask for that element. After that, all the subsequent mouseover
events are ignored, as they are registered on the mask, and not on the actual underlying elements.
I need to figure out a way around this.
Can anyone help me better Approach 2? Or suggest another approach?
Thanks, Sriram
Upvotes: 7
Views: 11148
Reputation: 2577
I changed the version by @jonatjano, such as
window.addEventListener('mouseover', function (e) {
updateMask(e.target);
});
function updateMask(target) {
let elements = document.getElementsByClassName("highlight-wrap")
let hObj
if (elements.length !== 0) {
hObj = elements[0]
} else {
hObj = document.createElement("div");
hObj.className = 'highlight-wrap';
hObj.style.position = 'absolute';
hObj.style.backgroundColor = '#205081';
hObj.style.opacity = '0.5';
hObj.style.cursor = 'default';
hObj.style.pointerEvents = 'none';
document.body.appendChild(hObj);
}
let rect = target.getBoundingClientRect();
hObj.style.left = (rect.left + window.scrollX) + "px";
hObj.style.top = (rect.top + window.scrollY) + "px";
hObj.style.width = rect.width + "px";
hObj.style.height = rect.height + "px";
}
Upvotes: 2
Reputation: 2049
This approach worked for me, applying the hover
rule to all *
elements.
*:hover {
background-color: #205081 !important;
}
The !important
ensures overwriting backgrounds set on individual HTML nodes. Keep CSS specificity mind, that is that the browser ignores this CSS rule if a similar rule with !important
is present on the hovered element.
Thanks to Louie, extending his answer.
Upvotes: 0
Reputation: 3
You can also use:
[attribute = value]:hover {
css declarations;
}
and use it on all divs which should have hover effect.
Upvotes: 0
Reputation: 71
@LouieAlmeda and @jonatjano are both right, i just want to add that if you don't like the re-arranges the elements on the page, you can add a border: 1px solid transparent
to elements and then on mouse-over/hover just change the border-color
Upvotes: 1
Reputation: 3738
@LouieAlmeda's answer is the way to go
But if you want to keep the mask div you can do : http://jsbin.com/filelavegu/1/edit?html,css,js,output
the only difference with your is that I added
hObj.style.pointerEvents='none';
at the mask creation
It makes the pointerevent pass through the div
Upvotes: 6
Reputation: 5612
You should do this in CSS and not in JS. Use the :hover
selector
.your-class:hover{
background-color: #205081;
}
Upvotes: 11