Hide all spans that are close to cursor on mouseover? [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
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?

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 onmouseout="hoverOut()" onmouseover="hover(this)" onmouseuop 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 onload="paint()">
<div id="painting">
</div>
</body>

Another approach without using ids 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>

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 class="container"></div>
</body>

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

Here is a bit diffrent and custumisable aproch:
// Variables
const ROW = 10; // Total number of rows
const COL = 35; // Total number of items in a row (i.e. columns)
const RANGE = 2; // Total number of items to be selected in each direction
const values = []; // To colect ids of items to be selected
// Utility Function
const push = (value) => {
if (value > 0 && value <= ROW * COL && !values.includes(value)) values.push(value);
};
// Add items in the root div
const root = document.querySelector("#root");
for (let i = 0; i < ROW; i++) {
root.innerHTML += `<div id="row-${i + 1}"></div>`;
for (let j = 0; j < COL; j++) {
document.querySelector(`#row-${i + 1}`).innerHTML += `<span id="item-${COL * i + (j + 1)}">#</span>`;
}
}
// Add class to the items as per the RANGE
root.addEventListener("mouseover", (e) => {
values.length = 0;
const id = e.target.id;
if (!id.includes("item-")) return;
const current = +id.replace("item-", "");
push(current);
for (let i = -RANGE; i < RANGE; i++) {
push(current + i);
for (let j = -RANGE; j <= RANGE; j++) {
push(current + COL * i + j);
push(current - COL * i + j);
}
}
for (let i = 0; i < values.length; i++) {
const item = document.querySelector(`#item-${values[i]}`);
item.classList.add("selected");
}
});
// Remove class from the items as per the RANGE
root.addEventListener("mouseout", () => {
for (let i = 0; i < values.length; i++) {
const item = document.querySelector(`#item-${values[i]}`);
item.classList.remove("selected");
}
});
/* Just for styling purposes */
body {
background-color: #111;
color: #fff;
}
#root [id*="item-"] {
padding: 1px;
}
/* Styles for the selected item */
#root [id*="item-"].selected {
/* color: transparent; */ /* 👈 Use this to get your intended effect */
color: #ffa600;
}
<div id="root"></div>

Related

selectionSort Javascript animation

I'm trying to build a visual representation of some famous sorting algorithms in javascript, but I can't understand why my code doesn't print each iteration even if the print function is in the for loop. I only get the final result.
This is the sorting function, in particular the selection sort algorithm:
function selectionSort(array) {
var i, j, min_idx;
let n = array.length;
for (i = 0; i < n-1; i++)
{
min_idx = i;
for (j = i + 1; j < n; j++)
{
if (array[j] < array[min_idx])
{
min_idx = j;
}
}
var temp = array[min_idx];
array[min_idx] = array[i];
array[i] = temp;
printArray(array);
}
}
And this is the printing function:
function printArray(array) {
document.getElementById('container').innerHTML ='';
for(let i = 0; i < array.length; i++)
{
document.getElementById('container').innerHTML += '<div class = "column '+i+'" id = "'+i+'" style = "height: '+array[i]+'px;"></div>';
}
}
Thank you a lot
It's what #Bravo states in the comments. The screen is updates at least 60 times per second, but it takes less time to do the sorting. So you need to add a timeout in a recursive loop so you can actually see the animation.
I replaced the first for loop with this recursive loop. I think the code it self-explanatory.
I did some optimization in your printArray(), where it takes time to constantly doing DOM changes. Instead, loop through to create a text string and then add it once to #container.innerHTML. There were also some faulty thinking in the value that you gave the visualized divs, where you only added the order (i), instead of adding the actual value (array[i]).
const iterationLegend = document.getElementById('iterations');
const containerDiv = document.getElementById('container');
const ANIMATION_SPEED = 1000;
const RESTART = 0;
var firstIteration;
function startSelectionSort(array) {
firstIteration = RESTART;
selectionSort(array);
}
function selectionSort(array) {
let min_idx = firstIteration,
n = array.length;
for (let j = firstIteration + 1; j < n; j++) {
if (array[j] < array[min_idx]) {
min_idx = j;
}
}
var temp = array[min_idx];
array[min_idx] = array[firstIteration];
array[firstIteration] = temp;
visualizeArray(array);
iterationLegend.textContent = 'iteration ' + firstIteration;
if (firstIteration < n - 1) {
firstIteration++;
setTimeout(selectionSort.bind(this, array), ANIMATION_SPEED);
} else {
iterationLegend.textContent = 'Done';
}
}
function visualizeArray(array) {
let elementStr = '';
let value = 0;
for (let i = 0; i < array.length; i++) {
value = array[i];
elementStr += `<div class="column${value}" data-value="${value}"></div>`;
}
containerDiv.innerHTML = elementStr;
}
startSelectionSort([2, 3, 5, 5, 1, 1, 1, 1, 4]);
fieldset {
display: inline;
}
#iterations {
font-size: 13px;
text-transform: uppercase;
}
#container {
display: inline-flex;
}
#container > div {
width: 10px;
height: 100px;
margin: 2px 1px 0px;
}
.column1 {
background-color: brown;
}
.column2 {
background-color: black;
}
.column3 {
background-color: teal;
}
.column4 {
background-color: red;
}
.column5 {
background-color: indigo;
}
<fieldset>
<legend id="iterations">Iterations</legend>
<div id="container"></div>
</fieldset>

How to create a css grid with javascript with a variable number of "cells"?

I am trying to (in Javascript)
Style a div as a CSS Grid in which the number of rows and columns can change (dependent on a future prompt element).
Default this Grid to be 16x16 when the page first loads.
Create and append divs to this grid that will fill all the grid areas. e.g. in the 16x16 grid, there will be 256 created divs.
I tried to do this via a loop. As demonstrated below:
<div class = "grid"> </div>
.grid{
height: 70vh;
width: 80vw;
}
const gridContainer = document.getElementsByClassName("grid");
let rowtot = 16;
let celltot = rowno * rowno;
gridContainer.style.display = "grid";
gridContainer.style.gridTemplateRows = `repeat(${rowtot}, 1fr)`;
gridContainer.style.gridTemplateColumns = `repeat(${rowtot}, 1fr)`;
let row = 1;
let column = 1;
for(let i = 1; i <= celltot; i++){
let cell = document.createElement("div");
cell.style.border = "1px solid black";
cell.style.gridRow = row;
cell.style.gridColumn = column;
column +=1;
if (column == 16){
row += 1;
column = 1;
}
gridContainer.appendChild(cell);
}
Maybe that way ;)
let gridContainer = document.querySelector('.grid');
let rowtot = 16;
let celltot = rowtot * rowtot;
gridContainer.style.display = 'grid';
gridContainer.style.gridTemplateRows = `repeat(${rowtot}, 1fr)`;
gridContainer.style.gridTemplateColumns = `repeat(${rowtot}, 1fr)`;
let row = 1;
let column = 1;
for (let i = 1; i <= celltot; i++) {
let cell = document.createElement('div');
cell.style.border = '1px solid black';
cell.style.gridRow = row;
cell.style.gridColumn = column;
cell.textContent = i;
column += 1;
if (column === rowtot + 1) {
row += 1;
column = 1;
}
gridContainer.appendChild(cell);
}
.grid {
height: 70vh;
width: 80vw;
}
<div class="grid"> </div>

Get paragraph alignment value

I wanted to get the value of the paragraph alignment to put in the IF condition. The example below fails and does not run
Here is the code
const paragraph = document.getElementsByTagName('p');
for (let i = 0; i < paragraph.length; i++) {
if (paragraph[i].getAttribute('align').value == 'center') {
console.log('paragraph' + i + 'centered')
}
}
<p align="center">this is a paragraph!</p>
As you have noted, the value property does not exist in the attribute. How can I get the value of align, whether it is "center, left, right .."?
const paragraph = document.getElementsByTagName('p');
for (let i = 0; i < paragraph.length; i++) {
if (paragraph[i].getAttribute('align') == 'center') {
console.log('paragraph' + i + 'centered')
}
}
<p align="center">this is a paragraph!</p>
But the align attribute is old and deprecated. It's better to use text-align even if it is more code to read the styles.
const paragraph = document.getElementsByTagName('p');
for (let i = 0; i < paragraph.length; i++) {
let computed = window.getComputedStyle(paragraph[i], null);
let alignment = computed.getPropertyValue('text-align')
if (alignment == 'center') {
console.log('paragraph' + i + 'centered')
}
}
p {
text-align: center;
}
<p>this is a paragraph!</p>

Inline style edited with JavaScript is being ignored

I'm trying to use document.getElementByClassId().style.color to change the color of an element in my HTML document. I can see that it does add an inline style tag to the element but they are seemingly ignored by the browser.
I tried using the !important tag but that changed nothing. Here is my HTML document. I changed my CSS for the elements I want to modify into inline tags but they are ignored as well. Everything else works so far.
Here is my code (I'm a beginner please go easy on me :/).
//Only gets used to make the rows array.
var cells = [];
//Array that contains the entirety of the table element.
var rows = [];
//Random number to be used to color a random cell.
var rand = 0;
//Unique ID number that gets applied to each cell on the gameboard.
var id = 0;
//Calls cellMake.
var makeBoard = function(size) {
cellMake(size);
}
//Adds opening and closing tags at the beginning and end of each string then writes them to the rows array.
var rowMake = function(num) {
for(c = 0; num > c; c++) {
rows[c] = '<tr>' + cells[c] + '</tr>';
}
writeBoard();
}
//Writes cell elements to the cells array based on how many columns the makeBoard function was called for.
var cellMake = function(num) {
for(a = 0; num > a; a++) {
cells[a] = '';
for(b = 0; num > b; b++) {
cells[a] += '<td id="' + id + '" class="pixel">';
id++;
}
}
rowMake(num);
}
//Chooses random pixel from the board and sets its color.
var choosePixel = function() {
rand = Math.round(Math.random() * rows.length * rows.length);
console.log(rand);
document.getElementById(rand).style.color = 'red';
}
//Writes each element of the rows array onto the HTML document.
var writeBoard = function() {
for(d = 0; rows.length > d; d++) {
document.getElementById('gameboard').innerHTML += rows[d];
}
choosePixel();
}
window.onload = function() {
makeBoard(50);
}
<!DOCTYPE html>
<html>
<head>
<title>Can I write JS?</title>
<script src="script.js"></script>
<style>
body {
text-align: center;
background-color: black;
}
#gameboard {
display: inline-block;
margin: 0 auto;
padding-top: 5%;
}
.pixel {
height: 5px;
width: 5px;
}
</style>
</head>
<body>
<table id="gameboard"></table>
</body>
</html>
You have to style the backgroundColor instead of color, because the cells don't contain any text, to which the color would be applied to
//Only gets used to make the rows array.
var cells = [];
//Array that contains the entirety of the table element.
var rows = [];
//Random number to be used to color a random cell.
var rand = 0;
//Unique ID number that gets applied to each cell on the gameboard.
var id = 0;
//Calls cellMake.
var makeBoard = function(size) {
cellMake(size);
}
//Adds opening and closing tags at the beginning and end of each string then writes them to the rows array.
var rowMake = function(num) {
for(c = 0; num > c; c++) {
rows[c] = '<tr>' + cells[c] + '</tr>';
}
writeBoard();
}
//Writes cell elements to the cells array based on how many columns the makeBoard function was called for.
var cellMake = function(num) {
for(a = 0; num > a; a++) {
cells[a] = '';
for(b = 0; num > b; b++) {
cells[a] += '<td id="' + id + '" class="pixel"></td>';
id++;
}
}
rowMake(num);
}
//Chooses random pixel from the board and sets its color.
var choosePixel = function() {
rand = Math.round(Math.random() * rows.length * rows.length);
console.log(rand);
document.getElementById(rand).style.backgroundColor = 'red';
}
//Writes each element of the rows array onto the HTML document.
var writeBoard = function() {
for(d = 0; rows.length > d; d++) {
document.getElementById('gameboard').innerHTML += rows[d];
}
choosePixel();
}
window.onload = function() {
makeBoard(50);
}
<!DOCTYPE html>
<html>
<head>
<title>Can I write JS?</title>
<script src="script.js"></script>
<style>
body {
text-align: center;
background-color: black;
}
#gameboard {
display: inline-block;
margin: 0 auto;
padding-top: 5%;
}
.pixel {
height: 5px;
width: 5px;
}
</style>
</head>
<body>
<table id="gameboard"></table>
</body>
</html>

Set a custom % of 1's into a 2D blank array, where the 1's are randomly shuffled?

I've been a long time lurker on Stack Overflow but I couldn't seem to find a suitable existing solution...
I'm learning JS and HTML, and I've been playing around with 2D arrays to make game board. So far I made a custom # of rows/columns for a game board with all white tiles (represented as 0 for now).
My goal is to use an input field for a % of black tiles (represented as 1) to fill up the board (2D Array), but the black tiles have to be randomly distributed/shuffled among it.
Here's what I've got so far..
https://jsfiddle.net/5pvm4mmy/6/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val()
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (i%2 == j%2) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}
Thanks in advance for any help or advice.
If you need a random selection of a predefined set of values, you can use a stack. Think of it as a deck of cards and you pick a random card each time from the number of card left in the deck. In this case you have only 2 values but you may want to set the number of black and white. For this you can use a pseudo stack.
var black = 29; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
If you have many options like a deck of cards you use an array.
Example of a random stack.
// create a deck
var cards = [];
for(var c = 0; c < 52; c++){
cards[c] = c;
}
function shuffle(cards){
var shuf = []; // to hold the shuffled deck
while(cards.length > 0){ // pick a random item, take it from the stack and
// put on the new stack until there are no items
// left
shuf.push(cards.splice(Math.floor(Math.random() * cards.length),1));
}
return shuf; // return shuffled deck
}
cards = shuffle(cards); // get shuffled deck.
Which will work for anything where you need to pick randomly from a predefined set. It only takes one pass and the set is as random as the random number generator
To show psuedo stack working ... Always has 60 black
var cont;
function draw(){
var rows = 15;
var cols = 15;
var black = 60; // 29 blacks the rest white
var white = (rows * cols) - black;
function getRandSquare(){
var select = Math.floor(Math.random() * (black + white));
if(select > black-1){
white -= 1;
return "white";
}
black -= 1;
return "black";
}
var bCount = 0;
cont = document.createElement("div");
for(var y = 0; y < rows; y++){
for(var x = 0; x < cols; x++){
var s = document.createElement("span");
s.className = getRandSquare();
if(s.className === "black"){
s.textContent = bCount;
bCount += 1;
}
s.style.top = ((y+2) * 20) + "px";
s.style.left = (x * 20) + "px";
s.style.width = "20px";
s.style.height = "20px";
cont.appendChild(s);
}
}
document.body.appendChild(cont);
}
document.body.onclick = function(){
document.body.removeChild(cont);
cont = null;
draw();
}
draw();
span {
position:absolute;
border : 1px solid;
font-size : small;
text-align : center;
}
.black {
background : black;
border-color :white;
color : white;
}
.white {
background : white;
border-color :black;
}
<h3>Click to randomise</h3>
Never mind. I got it done, thanks!
https://jsfiddle.net/5pvm4mmy/8/
function generateArray() {
var myNode = document.getElementById("table");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
rows = $("#rows-field").val();
cols = $("#cols-field").val();
concentration = $("#concentration-field").val();
source = $("#source-field").val();
target = $("#target-field").val();
var table = document.getElementById("table");
for (var i = 0; i < rows; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < cols; j++) {
var td = document.createElement('td');
if (concentration < Math.floor((Math.random() * 100) + 1)) {
td.className = "white";
} else {
td.className = "black";
}
tr.appendChild(td);
}
table.appendChild(tr);
}
document.body.appendChild(table);
}

Categories

Resources