Apply class to specific element based on button hover (with matching pairs) - javascript

As the title suggest I am trying to apply a class/styling to a specific element (in this case an SVG path) based on its corresponding button.
I have a map of the UK that is split up into regions and corresponding buttons to those regions, and I am trying to style a region based on hovering over a button.
So far I have a list of buttons and regions:
// England Buttons
const grBtn = document.querySelector(".eng-gr");
const seBtn = document.querySelector(".eng-se");
const eeBtn = document.querySelector(".eng-ee");
const emBtn = document.querySelector(".eng-em");
const yhBtn = document.querySelector(".eng-yh");
const neBtn = document.querySelector(".eng-ne");
const nwBtn = document.querySelector(".eng-nw");
const wmBtn = document.querySelector(".eng-wm");
const swBtn = document.querySelector(".eng-sw");
// England Region
const grRgn = document.querySelector(".greater-london");
const seRgn = document.querySelector(".south-east-england");
const eeRgn = document.querySelector(".east-england");
const emRgn = document.querySelector(".east-midlands");
const yhRgn = document.querySelector(".yorkshire-humber");
const neRgn = document.querySelector(".north-east-england");
const nwRgn = document.querySelector(".north-west-england");
const wmRgn = document.querySelector(".west-midlands");
const swRgn = document.querySelector(".south-west-england");
I know I can manually use a function for each button/region like so:
grBtn.addEventListener("mouseover", function () {
grRgn.classList.add("rgn-hover");
});
grBtn.addEventListener("mouseout", function () {
grRgn.classList.remove("rgn-hover");
});
But I am trying to figure out how I can do it with one (or a few) functions instead of each button/region (i will eventually be adding the rest of the UK).
Codepen of Project: https://codepen.io/MartynMc/pen/OJZWWer

Just an idea but if you can edit the HTML part and trasform this:
Greater London
Into this
Greater London
So for each button you specify the SVG class to highlight, you can just use event delegation on the buttons container like this:
let container = document.querySelector(".buttons");
container.addEventListener("mouseover", function (e) {
let ref = e.target.dataset.highlight;
if (ref) {
document.querySelector("." + ref).classList.add("rgn-hover");
}
});
container.addEventListener("mouseout", function (e) {
let ref = e.target.dataset.highlight;
if (ref) {
document.querySelector("." + ref).classList.remove("rgn-hover");
}
});
And that's all the JS code you need.
The parent receives the event from its children and if the child contains a data-highlight attribute, it finds a node with that class name and adds/removes the css class.

try this:
let arr = [
{ btn: grBtn, rgn: grRgn },
{ btn: seBtn, rgn: seRgn },
{ btn: eeBtn, rgn: eeRgn },
{ btn: emBtn, rgn: emRgn },
{ btn: yhBtn, rgn: yhRgn },
{ btn: neBtn, rgn: neRgn },
{ btn: nwBtn, rgn: nwRgn },
{ btn: wmBtn, rgn: wmRgn },
{ btn: swBtn, rgn: swRgn },
];
arr.forEach((item) => {
item.btn.addEventListener("mouseover", function () {
item.rgn.classList.add("rgn-hover");
});
item.btn.addEventListener("mouseout", function () {
item.rgn.classList.remove("rgn-hover");
});
});

Related

JS get DOM element within a Class, that was created by the Class?

I have a JS Class that generates some content then I have some event listeners listening to the newly-created content, right now it looks like that:
https://jsfiddle.net/2fa6k4jz/
const articles = [{ text: "abc" },{ text: "xyz" }]
class Page {
constructor(articles) {
this.articles = articles;
};
render() {
this.articles.forEach((article) => {
const art = document.createElement("article");
art.textContent = article.text;
document.body.appendChild(art);
})
}
}
const blog = new Page(articles);
blog.render();
document.querySelectorAll("article").forEach((article) => {
article.addEventListener("click", (e) => alert(e.target.textContent));
});
Is there a way to move my eventListener logic to the inside of the Class? I have tried moving const articles = document.querySelectorAll("article") to constructor but it returs null as the articles aren't already there...
Thanks for any hints!
Yes u can do move ur event listener logic to the inside class by adding ur addEventListner method to the class Page.
try this :
const articles = [{ text: "abc" }, { text: "xyz" }];
class Page {
constructor(articles) {
this.articles = articles;
}
render() {
this.articles.forEach((article) => {
const art = document.createElement("article");
art.textContent = article.text;
document.body.appendChild(art);
});
}
addEventListenerToArticles() {
document.querySelectorAll("article").forEach((article) => {
article.addEventListener("click", (e) => alert(e.target.textContent));
});
}
}
const blog = new Page(articles);
blog.render();
blog.addEventListenerToArticles();
You can attach the eventListener inside your render function
{ text: "abc" },
{ text: "xyz" }
]
class Page {
constructor(articles) {
this.articles = articles;
};
render() {
this.articles.forEach((article) => {
const art = document.createElement("article");
art.textContent = article.text;
art.addEventListener("click", (e) => alert(e.target.textContent));
document.body.appendChild(art);
})
}
}
const blog = new Page(articles);
blog.render();

Ghost image on drag and drop showing siblings

When I drag a div, the sibling at the bottom is showing inside the ghost image.
Illustration :
How to remove the next sibling, here the span "Hello" this one ?
I use Solidjs, and native js drag and drop.
Each component is draggable and droppable.
But a component cannot be drag inside itself or its children.
I listed the different properties and events I use for each component :
const onDrop: main.Attributes['onDrop'] = (event) => {
event.stopPropagation();
removeClass(parent, STRINGS.DROP_CLASS);
if (isDragging()) {
setView({ parent });
} else {
createView({ parent });
}
};
const onDragOver: main.Attributes['onDragOver'] = (event) => {
event.preventDefault();
event.stopPropagation();
// event.currentTarget.style.cursor = 'dragging';
};
const onDragEnter: main.Attributes['onDragEnter'] = (event) => {
event.preventDefault();
event.stopPropagation();
// event.currentTarget.style.cursor = 'dragging';
addClass(parent, STRINGS.DROP_CLASS);
};
const onDragLeave: main.Attributes['onDragLeave'] = (event) => {
event.preventDefault();
event.stopPropagation();
removeClass(parent, STRINGS.DROP_CLASS);
};
const draggable = true;
const setClass = partialCall(setAttribute, id, 'class');
const _backClass =
'!opacity-50 !scale-90 !origin-left !transition !duration-300 !ease-out';
const over = STRINGS.SHOW_BORDER;
// #region Events
const onDragStart: main.Attributes['onDragStart'] = (event) => {
setClass(`${classe} ${STRINGS.DRAG_CLASS}`);
event.stopPropagation();
(event.dataTransfer as DataTransfer).effectAllowed = 'uninitialized';
// event.currentTarget.style.cursor = 'dragging';
if (dragOnly) {
prepareForCreation({ classe, component, ..._props });
} else {
startDrag({ id });
}
setTimeout(() => {
if (dragOnly) {
setClass(`${cl} ${_backClass}`);
} else {
setClass(`${classe} ${_backClass}`);
}
}, 5);
};
const onMouseEnter: main.Attributes['onMouseEnter'] = (event) => {
event.stopPropagation();
!dragOnly && recursiveHoverClass(id);
};
const onMouseOver: main.Attributes['onMouseOver'] = (event) => {
event.stopPropagation();
!dragOnly && recursiveHoverClass(id);
};
const onMouseLeave: main.Attributes['onMouseLeave'] = () => {
removeClass(id, over);
};
const onDragEnd: main.Attributes['onDragEnd'] = () => {
setClass(dragOnly ? cl : classe);
stopDrag();
};
In its current condition your code is hard to reproduce because components are missing, you've posted event handlers only.
If it works for you, you can use solid-dnd, a lightweight and extensible drag and drop library for Solid JS: https://github.com/thisbeyond/solid-dnd

Testing input in MochaJS

I'm fairly new to the MochaJS testing framework and I'm trying to validate a dropdown menu in response to user input. I have a component with the following method:
inside my Search component:
createQueryInputEl() {
const inputEl = document.createElement('input');
Object.assign(inputEl, {
type: 'search',
name: 'query',
autocomplete: 'off',
});
inputEl.addEventListener('input', event =>
this.onQueryChange(this, event.target.value));
The onQueryChange() function creates a dropdown by creating an li element for each result like this:
createResultsEl(results) {
const fragment = document.createDocumentFragment();
results.forEach((result) => {
const el = document.createElement('li');
Object.assign(el, {
className: 'result',
textContent: result.text,
});
// Pass the value to the onSelect callback
el.addEventListener('click', (event) => {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.text);
this.displaySelected(this.inputEl, result.text)
});
fragment.appendChild(el);
});
return fragment;
}
In MochaJS, I'm trying to test the population of that dropdown like this:
it('should populate input on value selection', () => {
const input = document.querySelector('input');
input.value = "New"
input.dispatchEvent(new Event('input'));
const results = document.querySelectorAll('.result');
expect(true).toBeTruthy();
})
The problem is that results keeps coming up empty. When I enter that code into the console, results is a NodeList with each li element of the result.
How do I get MochaJS to simulate this behavior?

Cant get the attribute value of the order button in application dev tools(javascript)

window.addEventListener("DOMContentLoaded", function(e) {
const orderButtons = document.querySelectorAll("data-order");
orderButtons.forEach(function(button) {
button.addEventListener("click", function(e) {
const button = e.currentTarget;
const button = button.parentNode;
const order = {
id: button.getAtrribute("data-order"),
title: container.querySelector(".title").innerText,
price: container.querySelector(".price").innerText,
desc: container.querySelector(".desc").innerText,
};
localStorage.setItem("order", JSON.stringify(order));
const url = window.location.href.replace("pies.html", "order.html");
});
});
});
You have a mis-spell. It should be getAttribute, not getAtrribute

Variable is pushed, but it doesn't exist later

I have the following code:
const scenarioList = []
const randomScenario = () => {
return scenarioList[Math.floor(Math.random() * scenarioList.length--)]
}
class Scenario{
setBG(){
//screen.bg = this.bg
//screen.redraw()
}
write(text, buttons, callback){
//$('#gametext > span').html(`<span>${text}</span>`)
//input.setText(buttons)
//input.bindAll(callback)
}
constructor(imgsrc, text, actions, callback){
let img = new Image()
img.src = imgsrc
this.bg = img
this.text = text
this.actions = actions
this.callback = callback
scenarioList.push(this)
console.log(scenarioList)
}
}
I init the class the following (and this is in the global scope)
new Scenario('./bg/1.png', 'You look around and see a huge mountain, what do you do?',[
'Climb It!!',
'Walk around',
'Other Direction',
'Rest',
], [
() => {
alert('a')
},
() => {
alert('a')
},
() => {
alert('a')
},
() => {
alert('a')
},
])
And verify with console.log(scenarioList)
[Scenario]
So its appended, but when I later try to do a console.log() on the same variable it is the following:
[]
Code that causes it:
const startGame = () => {
alert('were here') // this executes at the correct time, but later then variable init.
let scn = randomScenario()
console.log(scenarioList)
scn.write()
scn.setBG()
}
I am not seeing why this would happen, anyone can give me a push in the right direction?
I've found the solution, this code actually removed the element from the array:
const randomScenario = () => {
return scenarioList[Math.floor(Math.random() * scenarioList.length--)]
}
instead I did this:
return scenarioList[Math.floor(Math.random() * scenarioList.length -1)]

Categories