How do I swap cells using two Onclick Events in Javascript - javascript

I am trying to swap two cells using two separate click events in javascript. The problem is that the values stored by the first click event is overwritten by the second click event, and the console shows me that the StringAdjacent value stored for the second click event has been overwritten. This is my code:
//Listen to a Set of Click Events to Swap Cells
document.getElementById('board').addEventListener("click", function(e){
click1ID = event.target.id;
click1Class = event.target.className;
stringAdjacency1 = click1ID.replace('cell','')
console.log(stringAdjacency1);
document.getElementById('board').addEventListener("click", function(e){
click2ID = event.target.id;
click2Class = event.target.className;
stringAdjacency2 = click2ID.replace('cell','')
console.log(stringAdjacency2);
});
console.log(stringAdjacency1, stringAdjacency2);
});
function swapIds(click1ID, click1Class, click2ID, click2Class) {
//Are cells adjacent? If so, swap Cells
//Check the winning combinations to see if there's a new match;
//Swap cells;
});
Please help! Thank you.

You are adding a click event listener, which when fired by clicking the first cell causes another click event listener to be added. So when you click the second cell, it will fire the first event listener (overwriting the value), plus the second listener you added after the first one.
You only need to register a single listener who's function can handle the logic:
Something like this should work (didn't test):
var firstCell = null;
document.getElementById('board').addEventListener("click", function(e){
if(!firstCell) {
firstCell = e.target;
} else {
var secondCell = e.target;
// do whatever logic you want
// reset first cell
firstCell = null;
}
});
This will set firstCell to be the first cell clicked, then the second click it will no longer be null, so it will go into the else condition and you can do whatever you want. Then you'll reset firstCell so the entire interaction can be repeated.

Related

onclick inside forEach, value parameter only refers to last element

I have a list of anchor in my html, I want to make their href editable.
Everything fine, but the validation step (last onclick) refers to the last anchor instead of the current one
var anchors = document.querySelectorAll('.home-content a');
var col = document.querySelectorAll('.home-content > article');
anchors.forEach((k)=> {
let linkpanel = document.getElementById('link-edit-panel'); //This element is a single div in my html
let linkpanelvalidate = document.getElementById('validate-link'); //the button inside the said div
let editinput = linkpanel.querySelector('input'); //the input inside this div
//For each anchors, I add a button that will let user show the "linkpanel" div to edit the href of this anchor
let editbut = document.createElement('div');
let linktxt = k.href;
editbut.classList.add('edit-but','toremove');
editbut.innerHTML = "<i class='fas fa-link'></i>";
//I put this new element to the current anchor
k.appendChild(editbut);
console.log(k); // this to show me the full list of anchors
/* PROBLEM START HERE */
//click on the "edit" button
editbut.onclick = ()=>{
console.log(k); //Here, it shows the good anchor!
}
//click on the "validate" button
linkpanelvalidate.onclick = ()=>{
console.log(k); //Here, it shows the very last anchor...
}
});
I tried to put the element inside a constant
const ttt = k;
It does not change a thing.
Thank you for your help
We are facing here a classical forEach bubbling misunderstand (and I was blind not to see it)
When the click on the validate button occures, the call is made from the "main" bubble (outside the loop function if you need to picture it) so naturaly, it returns the last occurrence of the loop when we print the value in the console for example.
Solution
There is many solutions, you can store these values in an array to use each of them later
var arr = [];
node.forEach((v)=>{
arr.push(v);
});
Or, you don't want to deal with an array and want to keep it simple, like me, and you create your button during the forEach loop event, like this
node.forEach((v)=>{
let btn = document.createElement('button');
document.body.appendChild(btn);
btn.onclick = ()=> {
console.log(v); //it is the current value, not the last one
//you can create another button here and put his onclick here, the value will still remains etc
}
});

Dom Traversing the Dom with JavaScript but event not hitting the Html Element

I want to add delete functionality to a button on an Li element that is dynamically created by Javascript but I can't seem to be able to get the event listener to hit the target button.
I tried adding it right away by doing
var trashButton = document.getElementsByClassName("deleteListItemButton");
trashButton.addEventListener('click',removeLiItem);
but I got an unhandled exception error
var idForLiElement =1;
document.getElementById("addTaskId").addEventListener('click', function(){
var valueFromTextBox = document.getElementById("textBoxId").value;
if(valueFromTextBox) addItemToDo(valueFromTextBox);
});
function addItemToDo(valueFromTextBox){
var unorderedList = document.getElementById("toDoId")
var listElement = document.createElement("li");
listElement.className ="listItem";
listElement.innerHTML = valueFromTextBox;
listElement.id =Number(idForLiElement);
idForLiElement ++;
//puts the newest list element before the last element
unorderedList.insertBefore(listElement, unorderedList.childNodes[0]);
//creates the div that will contain both buttons in each list element
var buttonsContainer = document.createElement("div");
buttonsContainer.className ="listItemButtonContainer";
//creates the delete button and assigns it a class name
var deleteButton = document.createElement("Button")
deleteButton.className ="deleteListItemButton";
//creates the complete button and assigns it a class name
var completeButton = document.createElement("Button")
completeButton.className ="completeListItemButton";
//creates the delete image tag and assigns it a class name
var trashImageTag = document.createElement("i")
trashImageTag.className = "fa fa-trash fa-2x";
//creates the check mark button and assigns it a class name
var checkMarkImageTag = document.createElement("i")
checkMarkImageTag.className= "fa fa-check fa-2x";
//appends delete image tag to delete button
deleteButton.appendChild(trashImageTag);
//appends check mark image tag to complete button
completeButton.appendChild(checkMarkImageTag);
//appends delete button to button container
buttonsContainer.appendChild(deleteButton);
//appends complete button to button container
buttonsContainer.appendChild(completeButton)
//appends button container to list element
listElement.appendChild(buttonsContainer);
};
var trashButton = document.getElementsByClassName("deleteListItemButton");
for (i = 0; i < trashButton.length; i++) {
trashButton[i].addEventListener('click',removeLiItem)
};
function removeLiItem(e){
console.log(this)
};
I just want the event listener to hit console.log so I know the button is working
Attach the event to the element you've just created
deleteButton.addEventListener('click', function() { console.log('hit!') })
I want to add delete functionality to a button on an Li element that is dynamically created by javascript but I cant seem to be able to get the event listener to hit the target button.
The elements are dynamically created, so by the time the elements are created, the for loop where you add the event listeners has already executed.
This is what event propogation is for. Let the parent element (ul) listen to event clicks instead of having each child hold the responsibility. That way it doesnt matter how many children elements are added, momma (parent element) will always be listening for clicks.
You can do something like this:
// on your ul element
const itemList = document.querySelector(".item-list");
// listen for click on here
itemList.addEventListener('click', removeLiItem);
// you can access the actual element through the event's `target`
function removeLiItem (event) {
if (event.target.classList.contains('deleteListItemButton') {
// remove element
}
}

Form values are overwritten after change

I have a form that shows up when each row in a table is double clicked. The values of this form can be updated and the form should be submitted with all row changes. But each time I double click on a row and edit the values of that form for that row, the previous values I had changed get overwritten. In order to work around this, I tried adding all the changes to a map with the row id as the key and the values of the form as the value. But the form still won't update with the new values. Here is a fiddle to demonstrate what I mean:
https://jsfiddle.net/4fr3edk7/2/
If I double click on the row that says "Adam Smith" and change that name to John Doe, when I double click on the second row and then double Click on "Adam Smith" again, it should say "John" on the first textbox and "Doe" on the second one. But the new value never seems to save.
This code snippet loops through each key, then loops through each value of that key:
for(var i = 0; i<key.length; i++){
var getval = globalchanges[key[i]];
for(var k=0; k<getval.length; k++){
$("#input1").val(getval[0]);
$("#input2").val(getval[1]);
}
}
How can I get the new changes to save? (The table rows don't have to show the changes, just the textbox values). Any help would be appreciated.
First, as mentioned by #Taplar you are binding the click event multiple times. Your approach is close enough, the idea of storing the changes is valid. You should have 2 functions, store the changes on button click and the second one to retrieve the changes by id.
Updated Fiddle
This function will get the values of the form and will store in on a global object
function setMap(id){
var firstrow = $("#input1").val();
var secondrow = $("#input2").val();
globalchanges[id] = [firstrow,secondrow];
}
This other function will check if the global object has values for the passed id, if not, it will use the values on the row
function getMap(id, tr){
if(globalchanges[id] != undefined && globalchanges[id].length == 2){
$("#input1").val(globalchanges[id][0]);
$("#input2").val(globalchanges[id][1]);
}
else{
$("#input1").val($(tr).find('td').eq(1).text());
$("#input2").val($(tr).find('td').eq(2).text());
}
}
Please note there are also changes on the dbclick and click events, they should be separated
$("#table tr").dblclick(function(){
$("#txtbox-wrapper").css({"display" : "block"});
var id = $(this).find('td').eq(0).text();
$('#id').val(id);
getMap(id,this);
});
$("#savebtn").click(function(){
var id = $('#id').val();
setMap(id);
});
And that we added and additional input to store the id on the form.
You are going to need to rethink your logic because of this part
$("#table tr").dblclick(function(){
$("#txtbox-wrapper").css({"display" : "block"});
var id = $(this).find('td').eq(0).text();
$("#input1").val($(this).find('td').eq(1).text());
$("#input2").val($(this).find('td').eq(2).text());
$("#savebtn").click(function(){
addToMap(id);
});
});
-Every time- you double click a table row you are adding a new click binding to the savebtn element. This means if you double click both rows, when you click that button it will execute addToMap for both ids. You may have other issues with your logic relying on only two other inputs for multiple rows, but this double/triple/+ binding is going to bite you.
There are few changes required in your logic as well as implementation.
1: Do not bind save event inside row click.
2: You are selecting the value in row double click event from td element. You need to update this element to keep your logic working
3: Keep track of which row is getting updated.
Updated Code
var globalchanges = {};
var rowSelected = null;
$("#table tr").dblclick(function() {
$("#txtbox-wrapper").css({
"display": "block"
});
rowSelected = $(this).find('td').eq(0).text();
$("#input1").val($(this).find('td').eq(1).text());
$("#input2").val($(this).find('td').eq(2).text());
});
$("#savebtn").click(function() {
addToMap(rowSelected);
});
function addToMap(row) {
var array = [];
var changes = {};
var firstrow = $("#input1").val();
var secondrow = $("#input2").val();
array.push(firstrow, secondrow);
globalchanges[row] = array;
makeChanges(row);
}
function makeChanges(row) {
var key = Object.keys(globalchanges);
console.log(key);
$("#table tr td").each(function(k, v) {
if ($(v).text() == key) {
$(v).next().html(globalchanges[row][0]);
$(v).next().next().html(globalchanges[row][1]);
globalchanges = {};
}
});
}
Working fiddle : https://jsfiddle.net/yudLxsgu/

DOM: Delete newly created 'article' element with newly created delete button within onclick event?

I have one section element that contains one article element. Also, I have one input button with 'onclick' event. Whenever this event fired, a new article element appended to the section element with unique id.
The newArticle element contains a label, text box and a delete button. All these three elements get created within the on-click event.
document.getElementById("addRow").onclick = function () {
var newCustomerlbl = document.createElement("label");
newCustomerlbl.innerHTML = "Cutomer Name: ";
var newCustomertxt = document.createElement("input");
newCustomertxt.setAttribute("type", "text");
var delBtn = document.createElement("input");
delBtn.setAttribute("type", "button");
delBtn.setAttribute("value", "Delete");
delBtn.setAttribute("id", "btnDelete");
var newArticle = document.createElement("article");
newArticle.appendChild(newCustomerlbl);
newArticle.appendChild(newCustomertxt);
newArticle.appendChild(delBtn);
var customerSection = document.getElementById("customerRecords");
var customerArticles = customerSection.getElementsByTagName("article");
for (var i = 0; i < customerArticles.length; i++) {
var lastDigit = i + 1;
var newArticleValue = "article" + lastDigit;
newArticle.setAttribute("id", newArticleValue);
}
customerSection.appendChild(newArticle);
}
Now what I want is whenever user click upon the newly created appended delete button, only that particular article get deleted without effecting the rest of articles.
Here is the my jsFiddle code.
If you don't want to use jQuery you can add event listeners to your buttons:
delBtn.addEventListener('click', function () {
this.parentElement.remove();
}, false);
https://jsfiddle.net/3nq1v5e1/
You need to bind an event listener on the newly created delete button. Your example code about using $(this) suggest that you are using JQuery, but then again in the rest of the code you are not using any JQuery?
If you are using JQuery, things get real simple, just add something like
$(document).on('click','.btnDelete', function(){
$(this).closest('article').remove();
});
(and remember to give the deletebutton a CLASS rather than ID, as there will be multiple delete buttons).
If you are NOT using JQuery, you need to add the event listener EVERY TIME a new delete button is created
newArticle.appendChild(delBtn);
delBtn.onclick = function(.....
etc.

Attaching eventListener to dynamic elements in Javascript

I'm making a todo list and I have li and button tags added dynamically when adding a new list item. The button is an x which is supposed to remove the list item. I have tried several things but can't figure out how to make an eventListener for each individual x button and remove the corresponding list item when it is clicked.
The renderTodos function is where all of the dynamically added content is created. I have a data-index set to each button in which I was trying to use to access each button to attach an eventListener on each dynamic button, but I wasn't sure how to implement that. From what I have read there should be a way to do this using the currentTarget or target of the event but I don't understand how that works.
var input = document.querySelector('input[name=todoItem]'),
btnAdd = document.querySelector('button[name=add]'),
btnClear = document.querySelector('button[name=clear]'),
list = document.querySelector('.todo'),
storeList = [];
function renderTodos(){
var el = document.createElement('li'),
x = document.createElement('button');
listLength = storeList.length;
//Set text for remove button
x.innerHTML = 'x';
for(var i = 0; i < listLength; i++){
el.innerHTML = storeList[i];
list.appendChild(el);
x.setAttribute('data-index', i);
el.appendChild(x);
}
// check for correct data-index property on x button
}
function addTodos(){
storeList.push(input.value);
// Check that input is getting pushed to list array
console.log(storeList);
renderTodos();
}
function clearList(){
// make list empty
list.innerHTML = '';
storeList.splice(0, storeList.length);
//render empty list
renderTodos();
//Check that list array is empty
console.log(storeList);
}
btnAdd.addEventListener('click', addTodos);
btnClear.addEventListener('click', clearList);
Everything else on the list works so far I just can't figure out how to implement this eventListener.
One simple example can be
//a click hadler is added to #mylist which is already present in the dom
document.querySelector('#mylist').addEventListener('click', function(e) {
//assuming that the the `x` is in a span and it is the only span in the `li` we check for that, we can improve this check more to make sure we have actually clicked on the delete button
if (e.target.tagName == 'SPAN') {
//if so then since we know the structure we can delete the parent node of the target which is the span element
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
}
}, false);
//kindly forgive the use of jQuery here
for (var i = 0; i < 10; i++) {
$('<li />', {
text: i
}).append('<span class="x">X</span>').appendTo('#mylist');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul id="mylist"></ul>
This is a very basic implementation of event delegation, where the actual event is bound to an ancestor element but then we use the actual event target to determine whether to act on it. We can improve the if condition to test for an class for any other attribute!!!
You can add a listener to each button using something like:
x.innerHTML = '';
x.onclick = function(){
var node = this.parentNode;
node.parentNode.removeChild(node);
};
Or you can keep the renderTodos code as it is and delegate the remove to the parent UL:
// Add the listener
list.addEventListener('click', removeItem);
// The listener function
function removeItem(event) {
var node = event.target;
// Check that the click came from an X button
// better to check against a class name though
if (node.tagName &&
node.tagName.toLowerCase() == 'button' &&
node.innerHTML == 'x') {
node = node.parentNode;
node.parentNode.removeChild(node);
}
}
basically what you want to do is add an event on the parent container and wait for the event to bubble up and identify if the event originating is from your x mark and if it is then trigger the callback function.. This is the concept I think most of the libraries use..
Or use a library like jQuery, why solve a problem that has already been solved by others.

Categories

Resources