Function not repeating after the first event - javascript

I've been trying to learn how to work with HTML, CSS and JavaScript with no prior experience with a any sort of programming, and I'm trying some things out as I learn.
I'm trying to get a click event where multiple divs on a list (two in this case) to either lose a specific class if they have it, and gain said class if they don't, all on the same click.
The first time it's clicked, it runs properly, but how do I get it to do this on every click?
I know there's easier ways to do this, but I really just want to experiment with this setup for now.
If anybody has an idea on why it won't switch classes again, or any tips(or insults) whatsoever, please feel free.
Here's the code:
const SignList = document.querySelectorAll('.sign')
console.log(SignList)
SignList.forEach((div) => {
div.addEventListener('click', () => {
if (div.classList.contains('active')) {
SignList.forEach((div) => {
div.classList.remove('active');
SignList.forEach((div) => {
if (!div.classList.contains('active')) {
div.classList.add('active')
}
})
})
}
})
})
html {
margin: 0px;
padding: 0px;
box-sizing: border-box;
height: 100%;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.sign {
position: absolute;
height: 100px;
width: 100px;
border: 5px solid black;
border-radius: 20px;
}
.red {
background-color: red;
display: none;
}
.blue {
background-color: blue;
display: none;
}
.active {
display: block;
}
<div class="sign red active"></div>
<div class="sign blue"></div>

I feel like your question and code are a little confusing in terms of exactly what behavior you want from the click. Also, you probably shouldn't nest so much and reuse the same variable name [like div here] - instead, it might be better to break it down by defining a function.
If you want to toggle a certain class in each div when any div is clicked, you might change your js to something like this:
const SignList = document.querySelectorAll('.sign')
console.log(SignList)
function toggleClass(tClass) {
//incase more div.sign might be added dynamically:
const sList = document.querySelectorAll('.sign')
sList.forEach((div) => {
if (div.classList.contains(tClass)) {
div.classList.remove(tClass);
} else {
div.classList.add(tClass);
}
})
}
SignList.forEach((div) => {
div.addEventListener('click', () => { toggleClass('active') })
})
On the other hand, if you want to toggle that class only if the clicked div contain the class, you can change the above code to :
function toggleClass(clickTarget, tClass) {
if (clickTarget.classList.contains(tClass)) {
const sList = document.querySelectorAll('.sign');
sList.forEach((div) => {
if (div.classList.contains(tClass)) {
div.classList.remove(tClass);
} else {
div.classList.add(tClass);
}
})
}
}
SignList.forEach((div) => {
div.addEventListener('click', (evt) => { toggleClass(evt.target, 'active') })
})
Then, it will only toggle if an "active" element is clicked on. It won't make a difference here, because the non-"active" elements are not visible, and there are only 2 elements affected, but if the class (for example) changed border color to green, only clicking on divs with green border would do anything....
(evt.target passes the object [that the event happened to] to the function in the listener and is more reliable than passing it as div like in your code. You could pass on the evt object itself to give the function more information about the event, but it's not necessary in this case.)
Hope this helps, and best of luck with your coding journey!

You should iterate each of the .sign and toggle it. That's how the code now reflects that.
const SignList = document.querySelectorAll('.sign')
// console.log(SignList)
SignList.forEach((div) => {
div.addEventListener('click', () => {
SignList.forEach((div) => {
if (div.classList.contains('active')) {
div.classList.remove('active');
} else {
div.classList.add('active')
}
})
})
})
html {
margin: 0px;
padding: 0px;
box-sizing: border-box;
height: 100%;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.sign {
position: absolute;
height: 100px;
width: 100px;
border: 5px solid black;
border-radius: 20px;
}
.red {
background-color: red;
display: none;
}
.blue {
background-color: blue;
display: none;
}
.active {
display: block;
}
<body>
<div class="sign red active"></div>
<div class="sign blue"></div>
</body>

Related

Style adjustments in a reactJS dropdown Menu

I'm currently building a ReactJS App, and I needed to implement a dropdown menu.
I currently have the menu built, but I need help for two elements :
The style of the menu
The position of the menu
1 - Style of the menu
The menu looks like that :
But when I hoover the menu, there is a gap between the hovering of a link and the link itselfs.
For example :
Here I hoover 'Rename', but styling come under it...
2 - Position of the menu
Simple question : How can I choose myself the position of the menu in my page ?
Neither relative to the parent element nor in a fixed page position ?
Here are my source codes :
VerticalDots.js
import React from "react";
import "./VerticalDots.css";
export default class VerticalDots extends React.Component {
state = {
status: false,
elements: [
"Rename",
"Duplicate",
"Archive",
"Delete Permanently"
]
}
buttonClick = (e, curstat) => {
e.stopPropagation();
this.setState({ status: curstat });
};
displayElements(){
if(this.state.status){
return(
<div className="show-options">
{this.state.elements.map((value, key) => {
return (
<div className="data-row">{value}</div>
);
})}
</div>
);
}
}
render() {
return (
<div className="dropdown-root">
<div className="text-box">
<div className="button" onClick={e => this.buttonClick(e, !this.state.status)}>
<img src={require("../imgs/3dots-vertical.png")} alt="NotFound"/>
</div>
{this.displayElements()}
</div>
</div>
);
}
}
VerticalDots.css
.text-box {
width: 100px;
height: 40px;
position: relative;
text-align: left;
}
.button {
text-align: right;
font-size: 13px;
}
.show-options {
height: 110px;
width: 150px;
border: 1px solid #7A7A7A;
border-radius: 4px;
position: relative;
background: #EBEBEB;
cursor: pointer;
overflow-y: scroll;
overflow-x: hidden;
z-index: 1;
}
.data-row {
height: 20px;
text-align: left;
/* margin: 0px 10px 0px 10px; */
color: #25073C;
border-radius: 4px;
}
.data-row:hover {
background-color: #1464F6;
color: white;
}
.drop-text {
margin-top: 10px;
margin-left: 5px;
position: absolute;
}
.column9:hover{
background-color: red;
}
https://codesandbox.io/s/cool-engelbart-tuz4y
I couldn't recreate your first issue, can you please clarify further on what's happening on your end? It might be that you have other styles included that are causing that issue.
Regarding issue number 2, normal css rules apply to .show-options which is a child of .text-box. I am not sure how you want to position it, but one suggestion would be to use flex, something like:
.text-box {
height: auto;
display: flex;
flex-direction: column
}
I solved the problem, a hidden line-height was linked 3 branches above.
Thank you for your time !

how to add button with editable title using jquery?

I have a simple section in which the user can add multiple buttons on click, I want these buttons names to be the editable meaning user can edit and save the button title as they wish.
Here is UI how it looks when use click add button
I want a user to be able to add button title by placing a mouse on enter button name.
When a user places a mouse on enter a button name a simple pop up text area will appear something like this.
$(document).ready(function() {
$('#btn').on('click', function() {
var buttonWithText = $("<div class='clickarea'>Enter button name</div>")
$(".main-container").append(buttonWithText);
})
})
.main-container {
height: 300px;
width: 400px;
background: red;
}
.clickarea {
height: 60px;
width: 50%;
/* margin: 20px; */
background: green;
display: flex;
justify-content: center;
align-items: center;
margin: 80px auto;
}
#btn {
cursor: pointer;
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-container">
</div>
<button id="btn">Add button</button>
For a very simple solution, you can use prompt:
$(document).ready(function() {
$('#btn').on('click', function() {
let text = prompt('enter button text:');
var buttonWithText = $("<div class='clickarea'>"+text+"</div>")
$(".main-container").append(buttonWithText);
})
})
.main-container {
height: 300px;
width: 400px;
background: red;
}
.clickarea {
height: 60px;
width: 50%;
/* margin: 20px; */
background: green;
display: flex;
justify-content: center;
align-items: center;
margin: 80px auto;
}
#btn {
cursor: pointer;
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-container">
</div>
<button id="btn">Add button</button>
If I understand correctly, you're desired functionality is something like below. You can achieve this by taking advantage of addEventListener and then creating a new button and adding it to the DOM on click:
EDIT:
I've updated my code based on your comment. I believe it now works according to the second scenario you have described. Let me know if this does not seem like the desired functionality.
function init() {
const buttonEl = document.querySelector('#button-el');
const buttonContainer = document.querySelector('#button-container');
const alertButtonText = e => alert(`You clicked: ${e.target.value}`);
const promptForText = e => {
const text = window.prompt('Enter button text');
if (text && text.trim().length) {
e.target.setAttribute('value', text);
e.target.removeEventListener('mouseover', promptForText);
e.target.addEventListener('click', alertButtonText);
}
}
buttonEl.addEventListener('click', e => {
const newButtonEl = document.createElement('input');
newButtonEl.setAttribute('type', 'button');
newButtonEl.addEventListener('mouseover', promptForText);
buttonContainer.appendChild(newButtonEl);
});
}
init();
#button-container>input {
height: 30px;
width: 40%;
background: green;
display: block;
align-items: center;
margin: 20px auto;
}
body {
background: red;
}
#button-el {
cursor: pointer;
margin: 20px;
}
<input id="button-el" type="button" value="Add button" />
<div id="button-container">
</div>

Javascript: How do i target selected elements in a slideshow when hovering over one element?

Alright, so I'm working on my own javascript slideshow which consist of cards. Right now I'm adding/looping through the cards and adding an eventlistener (mouseover and mouseout) to check if the user hovered over chosen card.
Now to the problem. I need to be able to target all of the cards (part 1, see image) which comes before the chosen card of the user and also all of the cards (part 2, see image) which comes after. But I have to target them individually. Basically the cards in part 1 will get one kind of styling and the cards in part 2 will get another one. The chosen card will get its own styling.
This is what I have so far. If someone could point me in the right direction, that would be great, thanks. I don't want to use any libraries, strictly javascript.
var cards = [];
cards = document.querySelectorAll('.card');
for (var i = 0; i < cards.length; i++) {
cards[i].addEventListener('mouseover', function() {
//Do something
console.log('Mouseover: Do something');
});
cards[i].addEventListener('mouseout', function() {
//Do something
console.log('Mouseout: Do something');
});
}
.container {
list-style: none;
margin: 0px;
padding: 0px;
}
.card {
display: inline-block;
background-color: #fff2cc;
width: 100px;
height: 150px;
color: #000;
text-align: center;
}
<ul class="container">
<li class="card">Card-1</li>
<li class="card">Card-2</li>
<li class="card">Card-3</li>
<li class="card">Card-4</li>
<li class="card">Card-5</li>
</ul>
You can select the particular card and apply class name using jquery.
var cards = [];
cards = document.querySelectorAll('.card');
for (var i = 0; i < cards.length; i++) {
cards[i].addEventListener('mouseover', function() {
//Do something
$(this).addClass('selected');
console.log('Mouseover: Do something');
});
cards[i].addEventListener('mouseout', function() {
//Do something
$(this).removeClass('selected');
console.log('Mouseout: Do something');
});
}
.container {
list-style: none;
margin: 0px;
padding: 0px;
}
.card {
display: inline-block;
background-color: #fff2cc;
width: 100px;
height: 150px;
color: #000;
text-align: center;
}
.selected{
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="container">
<li class="card">Card-1</li>
<li class="card">Card-2</li>
<li class="card">Card-3</li>
<li class="card">Card-4</li>
<li class="card">Card-5</li>
</ul>
you can even use simple css which will be comman for all the card.
when card class is hovered this css will execute.
.card:hover{
background-color: blue;
}
You can set class for group/part 1, current card and group/part 2 separately.
You can possibly also listen to bubbling phase of event instead of multiple listener registration.
Check.
let ul = document.querySelectorAll('ul')[0];
ul.addEventListener('mouseover', function(e) {
if(e.target.className.indexOf("card") === -1) { return; }
let currentFound = false;
document.querySelectorAll('.card').forEach(function(card) {
if(card === e.target) {
card.classList.add("current-card");
currentFound = true;
}
else
if(currentFound) {
card.classList.add("next-cards");
}
else {
card.classList.add("previous-cards");
}});
});
ul.addEventListener('mouseout', function() {
document.querySelectorAll('.card').forEach(
function(card) {
card.classList.remove("previous-cards");
card.classList.remove("next-cards");
card.classList.remove("current-card");});
});
.container {
list-style: none;
margin: 0px;
padding: 0px;
}
.card {
display: inline-block;
background-color: #fff2cc;
width: 100px;
height: 150px;
color: #000;
text-align: center;
}
.previous-cards {
background-color: crimson;
}
.next-cards {
background-color: darkred;
}
.current-card {
background-color: indianred;
}
<ul class="container">
<li class="card">Card-1</li>
<li class="card">Card-2</li>
<li class="card">Card-3</li>
<li class="card">Card-4</li>
<li class="card">Card-5</li>
</ul>
If you would like to preserve the colors until next hovering just remove the mouseout listener and put its logic to start of mouseover listener right after if block.
I would do this with CSS and it's sibling selector:
.card {
background-color: red;
}
.card:hover ~ .card {
background-color: green;
}
If you need to use JavaScript, use [...mouseEnterCard.parentElement.children].indexOf(mouseEnterCard) to get the element index and then loop over the elements with a lower/higher index.

How to add dynamic listener and get value of target

I've created a custom dropdown and would like to get the text content of the clicked element within.
Dropdown elements are created dynamically as are the event listeners but the listeners seem not to be working correctly.
Dropdown example:
I can see the listeners on each div within the dev tools.
Event listener of child div:
The first div in the dropdown fills the input with it's value but the others do not.
(function() {
let departments = ['Accounting', 'Human Resources', 'IT', 'Warehouse'];
let element = document.getElementById('dd-Department');
departments.forEach(v => {
let div = document.createElement('div');
div.appendChild(document.createTextNode(v));
div.addEventListener('click', () => {
element.parentNode.querySelector('input').value = v;
});
element.appendChild(div);
});
})();
.form-question {
display: flex;
flex-direction: column;
justify-content: center;
margin: 0 0 3rem;
min-height: 3rem;
}
.form-question__title {
color: #342357;
font-size: 1.5rem;
padding: 1rem;
}
.input-container {
border-bottom: solid 2px #333333;
position: relative;
}
input[readonly] {
cursor: pointer;
}
.input-container input {
border: none;
box-sizing: border-box;
outline: 0;
padding: .75rem;
position: relative;
width: 100%;
}
.input-container:focus-within .dropdown {
transform: scaleY(1);
}
.dropdown {
background: #ffffff;
box-shadow: 0 5px 12px #333333;
left: 0;
max-height: 300px;
overflow-y: auto;
padding: 0;
position: absolute;
right: 0;
top: calc(100% + 2px);
transform: scaleY(0);
transform-origin: top;
transition: transform .3s;
z-index: 10;
}
.dropdown div {
border-bottom: 2px solid #777777;
cursor: pointer;
padding: 8px;
z-index: 20;
}
.dropdown div:hover {
background: #dddddd;
font-weight: 800;
}
<div class="form-question">
<div class="form-question__title">
<span>Department</span>
</div>
<div class="form-question--dropdown input-container">
<input type="text" name="Department" readonly="readonly"></input>
<div id="dd-Department" class="dropdown"></div>
</div>
</div>
I also took a stab at event delegation, but could not get the text content of the clicked div. The target is the parent of the intended div, thus the text content was all child values combined.
let element = document.getElementById('dd-Department');
element.addEventListener('click', e => {
if (e.target && e.target.classList.contains('dropdown')) {
e.target.parentNode.parentNode.querySelector('input').value = e.target.textContent;
}
}, true);
Event Delegation on click of child div:
Am I missing something here?
UPDATE
Thank you #dawn for pointing out css as the problem.
I've worked around this by changing
.input-container:focus-within .dropdown
to
.input-container.active .dropdown
and adding the active class with javascript.
document.querySelectorAll('.input-container').forEach(v => {
v.onclick = () => v.classList.toggle('active');
});
Issue now is that on click of anything other than the input-container the dropdown is still active.
The following works but feels like a hack.
document.querySelectorAll('.input-container').forEach(v => {
v.addEventListener('focus', () => v.classList.add('active'), true);
v.addEventListener('blur', () => setTimeout(() => v.classList.remove('active'), 75), true);
});
Are there more elegant solutions?
This situation is a problem with css,When you click on the div,The first thing that triggers is "transform: scaleY(0)" and the ".dropdown" has invisible,so Cannot trigger click event.
Don't use input:focus-within to control the Visibilityof the drop-down box, because when you click the drop-down box, the input has lost focus.

with javascript onclick add classname to a div

I want to achieve with javascript something like when i clink on any of thumbnail (btn-1, btn-2 and btn-3) the specific class should be add to box div dynamically.
my code: JSFiddle
document.getElementById('btn-1').onclick = function() {
document.getElementById('box').className = 'bg-1';
}
#box {
background-color: darkgray;
width: 200px;
height: 200px;
}
.thumbnail {
width: 30px;
height: 30px;
border: 1px solid;
margin: 5px;
position: relative;
float: left;
}
#btn-1 {
background-color: red;
}
#btn-2 {
background-color: green;
}
#btn-3 {
background-color: blue;
}
.bg-1 {
background-color: red;
}
.bg-2 {
background-color: blue;
}
.bg-3 {
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box"></div>
<div class="thumbnail" id="btn-1"></div>
<div class="thumbnail" id="btn-2"></div>
<div class="thumbnail" id="btn-3"></div>
You javascript is working, but your CSS isn't.
You need to add !important as follows to .bg-1, .bg-2 and .bg-3
.bg-1 {
background-color: red !important;
}
Otherwise the id styling is taking preference over the class styling
You can see the classname is being added if you right click on the grey div and choose inspect element in Chrome.
Instead of bothering with classes, use simply a data- attribute like: data-bg="#f00"
$('[data-bg]').css('background', function () {
$(this).on('click', () => $('#box').css('background', this.dataset.bg));
return this.dataset.bg;
});
#box {
background: darkgray;
width: 120px; height: 120px;
}
[data-bg] {
width: 30px; height: 30px;
margin: 5px;
float: left;
}
<div id="box"></div>
<div data-bg="red"></div>
<div data-bg="#00f"></div>
<div data-bg="rgb(255,0,180)"></div>
<div data-bg="linear-gradient(to right, #E100FF, #7F00FF)"></div>
<script src="//code.jquery.com/jquery-3.1.0.js"></script>
You want to use jquery .addClass() function:
$('.myButton').addClass('myNewClass');
The function would probably look something like this:
$(function () {
$('.thumbnail').click(function() {
$('#box').addClass($(this).attr('id'));
});
})
You can get all the thumbnails as an array, and then iterate through the array and dynamically add an event listener to each, which will add the desired className to box when clicked:
var thumbnails = document.getElementsByClassName('thumbnail');
Array.from(thumbnails).forEach(function(thumbnail) {
var id = thumbnail.id;
thumbnail.addEventListener('click', function() {
document.getElementById('box').className = id.replace('btn', 'bg')
});
});

Categories

Resources