Customize dynamically created elements adding an option choosed via popup - javascript

I have a little code that creates elements (rectangles) and when I pass the mouse over them, a "customize yellow button appears on it". When I click this button, a popup with colors let us choose a colour to add in the selected rectangle.
Basically, I have 3 elements... click on 1 of them and choose a colour. This action, clones de tag and set it into the selected item. This works fine.
The problem appears when I click in the second item (or third)... I choose a new different colour but the action changes the selected rectangle and the sibling -applies to all elements that already have a cloned - (like propagation)...
I need to customize every single rectangle with its own colour and not all of them with the same. I pasted a little code here and a working (wrong) link in jsfiddle.
The action executes "on" cause the items are created dynamically (in this example I set them manually.
Can anybody help me? I don't understand what I'm doing wrong.
https://jsfiddle.net/martiniglesias/20Laxn84/2/
$(document).on("click","a.person",function (e)
{
e.preventDefault();
e.stopPropagation();
var elrel=$(this).attr('rel');
var elem=$("#ch_dndBoard1 span[data-id="+elrel+"]");
var elemrel=elem.attr("rel");
if (elemrel=="f1E")
{
$("body").append ("<div class='overlay'></div>").show();
$(".persE").fadeIn("fast");
$(".persE li").click(function(f)
{
f.preventDefault();
f.stopPropagation();
var ese=$(this).closest("li");
if ($(this).hasClass("nope"))
{
elem.find('b').fadeOut("slow",function() { elem.find('b').remove(); });
}
else
{
elem.find('b').remove();
var added=ese.find("b").clone();
added.css({"left":0+"px","top":+5.48+"px","position":"absolute"});
$(added).insertAfter(elem.find('em'));
}
$('.persE').fadeOut("fast",function(){ $(".overlay").remove(); });
});
}
return false;
});
I expect that every single rectangle can choose its own colour cloning it from the popup. For example, I want, rect1 blue, rect2 without color, rect3 red...
Thank you!
PS: Please, forgive my poor english :(

You have this issue because you are adding a click event listener to .persE li each time you click on a a.person.
You need to remove that listener when all your logic is over:
$(".persE li").click(function(f) {
// Your code
$(".persE li").off('click');
});
Be aware that if you listen an other click event with a different logic, that one will be destroyed too.
In order to avoid this, you need to reference your different logics in function:
const changeColorEvent = (e) => {
// Your code
$(this).off('click', changeColorEvent); // Here, "otherEvent" will still exist.
};
const otherEvent = (e) => {
// Different logic here
}
$(".persE li").click(changeColorEvent);
$(".persE li").click(otherEvent);

Related

Moving a div child from one parent to another after div selection and parent selection using event propagation

I'm building out the game Towers of Hanoi, i've got everything figured out except for how to move a div (my disk) from one parent(the rods) to another in the following sequence: click on disk(div/child node) to select, then click on rod(parent node) that you want the disk to move to.
I've tried using multiple event handlers within one another in order to solve the problem, as well as using removeEventHandler after the div has been moved to a new parent node. I've also tried stopPropogation to prevent bubbling, but neither have worked out for me.
// Moving the blocks from one container to the other
// use 'prepend' in order to insert new disk at the top of the parent node as the first child
function move() {
for (let i = 0; i < disks.length; i++)
disks[i].addEventListener('click', function (evt) {
// activeDisk = evt.target.dataset.value
// give red border on block when selected
disks[i].style.border = 'Red 2px Solid';
console.log(disks[i])
// after disk is selected, click on desired stack to move disk to
stackA.addEventListener('click', function (evt) {
stackA.prepend(disks[i])
win(evt)
})
stackA.removeEventListener('click', function(){
return console.log(stopped)
})
stackB.addEventListener('click', function (evt) {
stackB.prepend(disks[i])
win(evt)
})
stackB.removeEventListener('click', function(){
return console.log(stopped)
})
stackC.addEventListener('click', function (evt) {
stackC.prepend(disks[i])
win(evt)
})
stackC.removeEventListener('click', function(){
return console.log(stopped)
})
check(evt)
})
}
I'd like for my disks to move to the containers and then deselect in order to move them individually. I'm able to move them but when i select them and then try to deselect them to move a different disk independently it hasn't worked.
You can't removeEventListener like this, it will not work.
You have to create a listener function and use that to add or remove later.
function exampleFunction(){ // your logic here }
stackA.addEventListener("click", exampleFunction);
stackA.removeEventListener("click", exampleFunction);

How to trigger a class change event when the code that changes the class is out of reach

I need to trigger an event on a class when that class changes
The only known change noticed in the DOM is that the class obtains a second class (say the class is "selectable", it becomes "selectable selected")
https://jsfiddle.net/zn1xj7wb/1/
In this fiddle, the blue squares may be selected and the css change happens when the class changes (adds "selected")
The goal is to be able to do something in another part of my code like that:
$("[class*='selectable']").on('classChange', function() {
//do stuff like change the background color or add text
//alert("this selectable div has been selected");
});
I am unsure how to proceed as jquery has no event for a class change, and I cannot add "classChange" the trigger to the hidden part of the code that adds and removes the "selected" class for it to be picked up by my code.
EDIT: the reason I need the trigger to be the class change is that it is a graph that uses up the first click to change the class (select the node of the graph) and so a first click on the div of that class does not register, only the second time, and I cannot have to click twice to //do stuff.
I'm not sure I understand your problem, but what I would do is atach the event to the document, like this:
$(document).on("click",".selectable", function() {
//do your stuff here
});
Now, as I've read you need to do something right after you add the class "selected" to "selectable", so you could do it in the function by checking wether it has the class or not and then do your stuff after you add the class "selected".
$(document).on("click",".selectable", function() {
if($(this).hasClass("selected")){
$(this).removeClass("selected")
//do your stuff
}else{
$(this).addClass("selected")
//do some different stuff
}
});
EDIT: Okay, so that won't work (see comments). However, I was able to come up with another solution. While you could regularly scan the whole DOM for changes using an external library, in this instance, you can make the app more performant by limiting your scope to just the selectable items.
What the following code does (jsfiddle link below) is take an initial sampling of the selected elements on the page. Then, once per event loop, it re-samples those selected elements. For each element that wasn't there before, it triggers a custom event:
$(document).ready(function() {
$('.selectable').on('customSelectEvent', (e) =>{
console.log("hello, world!");
// Do your stuff here
});
// Get the starting list of selectable elements
var selecteds = $('.selected');
// Using setInterval to make sure this runs at the back of the event loop
setInterval(() => {
let loopSelecteds = $('.selected');
$.each(loopSelecteds, function(loopIndex, loopSelected) {
let alreadySelected = false;
$.each(selecteds, function(index, selected) {
if ($(selected).get(0) === $(loopSelected).get(0)) {
alreadySelected = true;
}
});
if (!alreadySelected) {
$(loopSelected).trigger('customSelectEvent');
}
});
selecteds = loopSelecteds;
}, 0);
})
Some things to note here:
setInterval(()=>{...}, 0) is being used to cast this operation to the back of the event loop, so it will evaluate once per turn. Use caution when doing this, because if you do it too much, it can impact performance.
$().get(0) === $().get(0) is testing the DOM elements to see if they are the same element. We don't want to trigger the event if they are. Credit: https://stackoverflow.com/a/19546658/10430668
I'm using $.each() here because it's intelligent enough to handle collections of jQuery objects, which other loops weren't (without some fiddling).
Someone spot check me on this, but you may be able to put the custom event listener elsewhere in the code.
JS Fiddle: https://jsfiddle.net/zn1xj7wb/15/
This is my first answer, which doesn't work in this use case. I'll include it so that users who aren't so stuck can benefit from it:
Is there any reason you can't bind another listener to the click event
and test if it's got the right class? Such as:
$(document).ready(function() {
$(".selectable").click((e) => {
const currentElement = $(e.currentTarget);
// This is a little tricky: don't run the code if it has the class pre-setTimeout()
if (currentElement.hasClass('selected')) {
return;
}
// Using setTimeout to cast the evaluation to the end of the event loop
setTimeout(()=>{
if (currentElement.hasClass('selected')) {
// Do your things here.
alert("selected!");
}
},0);
})
})

How to check if all divs have a specific background-color?

I've dynamically added a bunch of divs of a certain class to my page, and they change color when the user hovers over them. I want to trigger a specific function when the last one has been set to a specific background color, but after messing around for some time I am still unsuccessful. If anyone could provide some insight, I would be very appreciative. The code below is from my latest attempt.
var counter=0;
$(".orb").each(function () {
if ($(this).css("background-color") === "#ede74a") {
counter=counter+1;
}
}
if (counter === orbarray.length) { //all backgrounds have been set
executeFunction();
}
Would be simpler to add a class to the elements and keep track of class
$(document).on('mouseenter','.orb',function(){
var $orb = $(".orb");
$(this).addClass('hovered');
if($orb.filter('.hovered').length === $orb.length){
alert('All hovered!');
executeFunction();
}
});
It's not clear exactly what is happening with the specific colors mentioned but this could be adapted for multiple classes if need be.
Note that browsers don't store colors as hex values....they store them as either rgb or rgba depending on browser
It's not clear from the code you've provided what orbarray equals. It's also odd that you're checking to see if counter equals the length of that array outside of the $.each() callback.
But so you're saying you need to trigger a specific function when the last <div> has been hovered over. Sounds like you want a handler for the mouseenter event, and to then check whether all the elements have been hovered over in that handler. Also, instead of applying inline CSS, if you apply a class you'll have a handier way to check whether the element has been hovered.
Observe:
function checkOrbs() {
var $orbs = $('.orb'),
$activeOrbs = $orbs.filter('.active');
if ($orbs.length === $activeOrbs.length) {
executeFunction();
}
}
$('.orb').on('mouseenter', function(e) {
var $target = $(e.target);
$target.addClass('active');
checkOrbs();
});
And then just apply your background color via CSS:
.active {
background-color: #ede74a;
}

D3 remove click events from one element out of a selection

I have a class in D3 say: selectors and I need to remove the click event from the selection
d3.selectAll('.selectors').on('click',function(){
//Remove the currently clicked element from the selection.
});
Ive got two problems:
The removed element is supposed to be moved to a different part of the page and the I need the click event on it to be removed.
Also, would it be possible to reinsert the removed element into the selection on doing something else, like clicking on the removed element again?
Edit:
Found a solution for problem 1
d3.selectAll('.selectors').on('click',function(){
//Remove the currently clicked element from the selection.
d3.select(this).on('click',null);
});
Is this the right way? Or is there a more graceful method?
A Demo Fiddle
here is the updated jquery it will work for your case
$(document).ready(function(){
$(document).on('click','.selectors',function(e){
//$(document).off( 'click','.selectors');
if(e.target.onclick==null)
{
e.target.onclick=
function(){
void(0);
};
alert('test');
console.log('Hello');
}
});
});
For problem 1, the best method(as far as I know) is to redefine the click event in D3 itself:
d3.selectAll('.selectors').on('click',function(){
//Remove the currently clicked element from the selection.
d3.select(this).on('click',null);
});
For problem 2, however, once you turn a click event callback to null, the only way is to redefine the click event again, perhaps recursively:
function clickDefine() {
d3.selectAll('.selectors').on('click', function () {
//Remove the currently clicked element from the selection.
console.log('Hello')
d3.select(this).on('click', null);
setTimeout(function(){clickDefine();},1000)
});
}
This function makes the click event inactive for 1 second on click. And reactivates this again. I'm hoping this is an effective solution.

How defined in jQuery was it a regular click on the same element or double-click?

How can I define in jQuery was it a regular click on the same element or double-click?
For example we have element like this:
<div id="here">Click me once or twice</div>
And we need to perform different actions after regular click and double-click.
I tried something like this:
$("#here").dblclick(function(){
alert('Double click');
});
$("#here").click(function(){
alert('Click');
});
But, of course, it doesn't work, everytime works only 'click'.
Then, some people showed me this:
var clickCounter = new Array();
$('#here').click(function () {
clickCounter.push('true');
setTimeout('clickCounter.pop()', 50);
if (clickCounter.length > 2) {
//double click
clickCounter = new Array(); //drop array
} else {
//click
clickCounter = new Array(); //drop array !bug ovethere
}
});
Here we tried to set the interval between clicks, and then keep track of two consecutive events, but this have one problem.. it doesn't work too.
So, someone knows how to do this? or can someone share a link to the material, where I can read about it?
From QuirksMode:
Dblclick
The dblclick event is rarely used. Even when you use it, you should be
sure never to register both an onclick and an ondblclick event handler
on the same HTML element. Finding out what the user has actually done
is nearly impossible if you register both.
After all, when the user double–clicks on an element one click event
takes place before the dblclick. Besides, in Netscape the second click
event is also separately handled before the dblclick. Finally, alerts
are dangerous here, too.
So keep your clicks and dblclicks well separated to avoid
complications.
(emphasis mine)
What you are doing in your question, is exactly how it should be done.
$(".test").click(function() {
$("body").append("you clicked me<br />");
});
$(".test").dblclick(function() {
$("body").append("you doubleclicked me<br />");
});
It works and here is an demo for that.
Since, you want to detect separate single double click. There is a git project for this.
$("button").single_double_click(function () {
alert("Try double-clicking me!")
}, function () {
alert("Double click detected, I'm hiding")
$(this).hide()
})
It adds up events to detect single double clicks.
Hope it helps you now.

Categories