I am working on a project where I have several hundred spans next to each other, with a letter of the text in each span. When I hover over one of the spans, I want to hide it, as well as the other spans nearby.
It makes an image like this:
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@
@@@@@@@@@@@@@@
My goal is to hide all of the spans in a given distance away from the mouse, like this:
HTML
<span id="overlay-1">@</span>
...
<span id="overlay-142">@</span>
<span id="overlay-143">@</span>
I'm able to hide 1 of the spans by calling their ids on mouseover and changing the style to display=none
, but I want to hide all that are in close proximity to the mouse. Any ideas on what I should do?
CodePudding user response:
I tried to solve this through JS. Here is my code:
function paint() {
let txt = "";
for (let j = 0; j < 100; j ) {
txt = "<div>"
for (let i = 0; i < 100; i ) {
txt = `<span onm ouseout="hoverOut()" onm ouseover="hover(this)" onm ouseuop id="overlay-${i}-${j}">@</span>`
}
txt = "</div>"
}
document.getElementById('painting').innerHTML = txt
}
function hover(x) {
let id = x.id;
let i = x.id.split('-')[1];
let j = x.id.split('-')[2];
for (let a = -2; a <= 2; a ) {
for (let b = -1; b <= 1; b ) {
const elem = document.getElementById(`overlay-${i-a}-${j-b}`);
elem ? elem.style.opacity = 0 : null;
}
}
x.style.opacity = '0';
}
function hoverOut() {
for (let i = 0; i < document.getElementsByTagName('span').length; i ) {
document.getElementsByTagName('span')[i].style.opacity = 1;
}
}
<body onl oad="paint()">
<div id="painting">
</div>
</body>
CodePudding user response:
You could use a CSS solution - overwrite the adjacent characters with a pseudo element on the clicked character.
This snippet uses a monospace font and it's set line height and letter spacing as CSS variables so you can alter them as required.
function clicked(ev) {
ev.target.classList.add('obscure');
}
const container = document.querySelector('.container');
for (let i = 0; i < 200; i ) {
const span = document.createElement('span');
span.innerHTML = '@';
if (i % 10 == 0) {
container.innerHTML = '<br>';
}
container.appendChild(span);
}
container.addEventListener('click', clicked);
.container {
width: 50vw;
height: auto;
font-family: Courier, monospace;
--line-height: 20px;
--letter-spacing: 5px;
line-height: var(--line-height);
letter-spacing: var(--letter-spacing);
}
.container span {
position: relative;
margin: 0;
padding: 0;
}
.obscure::before {
content: '';
width: calc(5ch (6 * var(--letter-spacing)));
height: calc(3 * var(--line-height));
background-color: white;
position: absolute;
top: 0;
transform: translate(calc(-50% 0.5ch), calc(-50% (1ch)));
left: 0;
z-index: 1;
display: inline-block;
}
<body>
<div ></div>
</body>
CodePudding user response:
If I understand you right, you want the span to vanish, but you don't want its space to also vanish. display:none is the wrong solution. you need visibility:hidden.
hiding the hovered element and elements before and after it is easy. the difficulty is in hiding element that are above or below it.
to do that, you would need to do some math.
assuming the answer doesn't need to be exact, you could do it something like this:
- calculate the centre positions of all spans and keep them in an array (so you don't need to recalculate every time)
- when a span is mouse-entered, check the array and calculate all spans that are within radius r of that span's centre point - or just above/below/left/right - whatever works.
- create a new array of spans that should be hidden
- check all hidden spans - if any of them are not in that new array, unhide them (visibility:visible)
- finally, go through the new array and set visibility:hidden on all spans in that array
CodePudding user response:
Another approach without using id
s would be to use Element.getBoundingClientRect()
to get the size and position of the hovered element and then use Document.elementFromPoint()
inside a loop to access elements near the hovered one:
const main = document.querySelector('main')
for (let i = 0; i < 800; i ) main.innerHTML = '<span>@</span>'
const spans = document.querySelectorAll('span')
const areaWidth = 50
const areaHeight = 50
const hidden = []
function getElements(currentSpan, color) {
const { top, right, bottom, left, width, height } = currentSpan.getBoundingClientRect()
for (let col = left - areaWidth / 2; col < right areaWidth / 2; col = width || 14) {
for (let row = top - areaHeight / 2; row < bottom areaHeight / 2; row = height || 14) {
const el = document.elementFromPoint(col, row)
if (el?.tagName === 'SPAN') {
el.style.color = color
hidden.push(el)
}
}
}
}
spans.forEach(span => {
span.addEventListener('mouseover', () => getElements(span, 'transparent'))
span.addEventListener('mouseout', () => {
hidden.forEach(el => (el.style.color = ''))
hidden.length = 0
})
})
main {
display: flex;
flex-wrap: wrap;
width: 640px;
cursor: default;
}
<main></main>