Delete function doesn't work properly. What's wrong? - javascript

This is my HTML(list of people)
<button class="btn btn-primary btn-block" id="add_user">Add new user</button>
<ul id="users_list" contenteditable="true">
<li class="user_data">
<img src="http://img.dunyanews.tv/blog_user_images/anchors//15_user_icon.png" alt="44user" class="user_image" />
<div class="user_data_id text-left">Id:></div>
<div class="user_data_username text-left">Username:</div>
<div class="user_data_firstname text-left">Firstname:</div>
<div class="user_data_lastname text-left">Lastname:</div>
<button class="btn btn-danger delete_user">X</button>
</li>
</ul>
This is my Javascript (one function for adding and one for deleting). First work well, but second (deleting) - delete only first (or [0]) element
var addUser = document.getElementById('add_user'),
usersList = document.getElementById('users_list'),
userTemplate = document.getElementsByClassName('user_data');
deleteUser = document.getElementsByClassName('delete_user');
//Function for adding elements
addUser.addEventListener('click', (function() {
var newUser = userTemplate[0].cloneNode(true);
usersList.appendChild(newUser);
}));
//Function for deleting element
for (i = 0; i < deleteUser.length; i++) {
(function(e) {
deleteUser[e].addEventListener('click', (function() {
usersList.removeChild(userTemplate[e]);
}));
})(i);
}

Try this code:
I changed the display on the default template to hidden so we will also have one user to clone from. Otherwise, the user can delete all and we have nothing to clone from.
<button class="btn btn-primary btn-block" id="add_user">Add new user</button>
<ul id="users_list" contenteditable="true">
<--! This display none is important -->
<li class="user_data" style="display: none;">
<img src="http://img.dunyanews.tv/blog_user_images/anchors//15_user_icon.png" alt="44user" class="user_image" />
<div class="user_data_id text-left">Id:></div>
<div class="user_data_username text-left">Username:</div>
<div class="user_data_firstname text-left">Firstname:</div>
<div class="user_data_lastname text-left">Lastname:</div>
<button class="btn btn-danger delete_user">X</button>
</li>
</ul>
To make sure we are deleting the correct user, I added an array of names for testing.
var names = ['Noah', 'Sophia', 'Liam', 'Emma', 'Jacob', 'Olivia', 'Mason', 'Isabella', 'William', 'Ava', 'Ethan', 'Mia', 'Michael', 'Emily', 'Alexander', 'Abigail', 'Jayden', 'Madison', 'Daniel', 'Elizabeth'];
var addUser = document.getElementById('add_user'),
usersList = document.getElementById('users_list'),
userTemplate = document.getElementsByClassName('user_data');
deleteUser = document.getElementsByClassName('delete_user');
//Function for adding elements
addUser.addEventListener('click', (function() {
var newUser = userTemplate[0].cloneNode(true);
// add name to user so we can test.
newUser.getElementsByClassName('user_data_firstname')[0].innerHTML += ' ' + names.pop();
// make this user visible
newUser.style.display = '';
// Wire click event to remove this user.
newUser.getElementsByClassName('delete_user')[0].addEventListener('click', (function() {
usersList.removeChild(newUser);
}));
usersList.appendChild(newUser);
}));
// Add one user
addUser.click();
Note: You start with one user and you loop through the user list to add the remove, so when new users are added, the click to remove event is not wired.
I removed the loop and wire it as we add a new user.
Obviously our code will fail if we click on add users and we run out of names, but that's enough for our test.

Related

Deleting class from an element

I'm making a task board project.
Must say I'm using only HTML, CSS, JS, and nothing else right now.
I'm making a fade-in effect to the newly added note (ul element), and I would like to delete the fade-in class from the previously added note.
this is a chunk of my code that displays the note inside the div.
function displayAllTasks(allTasks){
taskNotesDiv.innerHTML = "";
for(const task of allTasks){
const index = allTasks.indexOf(task);
const note = `
<div class"noteDiv">
<ul class="fadeInNote">
<button type="button" onclick="deleteTask(${index})">
<i class="fa-solid fa-trash deleteButton"></i>
</button>
<li>Task: ${task.task}</li>
<li>Description: ${task.textArea}</li>
<li>Date: ${task.date}</li>
<li>Time: ${task.time}</li>
</ul>
</div>
`
taskNotesDiv.innerHTML += note;
}
}
I tried already another function to delete it but with no success.
any help would be appreciated!
There can be multiple approaches, but my approach is to create element using document.createElement . The modified JS will become:
function displayAllTasks(allTasks) {
last_ul = null; // store the last ul element added
taskNotesDiv.innerHTML = "";
for (const task of allTasks) {
const index = allTasks.indexOf(task);
let noteDiv = document.createElement('div');
noteDiv.classList.add('noteDiv');
note_ul = document.createElement('ul');
note_ul.classList.add('fadeInNote');
note_ul.innerHTML = `
<button type="button" onclick="deleteTask(${index})">
<i class="fa-solid fa-trash deleteButton"></i>
</button>
<li>Task: ${task.task}</li>
<li>Description: ${task.textArea}</li>
<li>Date: ${task.date}</li>
<li>Time: ${task.time}</li>`
noteDiv.appendChild(note_ul);
// if it is not the first element, then remove the class from previous
if (last_ul != null) {
last_ul.classList.remove('fadeInNote');
}
last_ul = note_ul; // this becomes previous for next iteration
taskNotesDiv.appendChild(noteDiv);
}
// remove class of the last element
if (last_ul != null) {
last_ul.classList.remove('fadeInNote');
}
}

JavaScript removing list elements by class name

I am having an issue with my javascript function in regards to removing elements. What I have below is two lists inside a div menu with each <li> marked by a class according to the category of drink and the drinks themselves. I also have a button that opens a modal box where the user would type in a drink category to remove. I decided to approach this by naming <li> with classes by the drink category and then taking a js function to get the elements by class name from the input text node and to remove those elements with what the user typed.
<div class = "menu">
<ul>
<li class = "coffee">french press</li>
<li class = "tea"><a href="#">english breakfast/a></li>
<li class = "milk">whole</li>
</ul>
<ul>
<li class = "coffee">dark roast</li>
<li class = "tea">green tea</li>
<li class = "milk">two percent</li>
</ul>
</div>
<button type="button" id ="openmodal">Click Me!</button>
<div id="myDeleteModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<h1 class="modal-title">
<b>Type the drink category you want to remove </b>
</h1>
<input id="deletedrinktext" type="text" />
<button id="delete">Remove</button>
<button type="button" class="btn btn-defaultdeletedrink" id="closedbtn" data-dismiss="modal" onclick="">Close</button>
</div>
</div>
<script>
var modal = document.getElementById("myDeleteModal");
// Get the button that opens the modal
var btn = document.getElementById("openmodal");
var closebtn = document.getElementById("closedbtn");
// When the user clicks the button, open the modal
btn.onclick = function () {
modal.style.display = "block";
}
closebtn.onclick = function (event) {
modal.style.display = "none";
}
</script>
<script>
(function () {
document.querySelector('#delete').addEventListener('click', function () {
let inputRemover = document.querySelector('#deletedrinktext');
let itemRemover = document.createTextNode(inputRemover.value);
let listRemover = document.getElementsByClassName(itemRemover);
for (var i = 0; i < listRemover.length; i++) {
if (document.contains(listRemover)) {
listRemover[i].remove();
}
}
inputRemover.value = ""; // clear input
});
})();
</script>
So what I want to replicate is a user would open the modal box, type in coffee and click remove. This would remove from the document the following two elements:
<li class = "coffee">french press</li>
<li class = "coffee">dark roast</li>
This function isn't working so far and I am not sure if there is an error in my JS in getting each element or if going with the class approach is not the way to go about it? When I type in the name of the category just like written in my HTML, the element in the list still displays.
remove() is not a function of Array in javascript. I think your best approach would be to just not display the elements you want to remove. To do just change your handler function to this:
let inputRemover = document.querySelector('#deletedrinktext');
let listRemover = document.getElementsByClassName(inputRemover.value);
for (let i = 0; i < listRemover.length; i++) {
if (document.contains(listRemover[i])) {
listRemover[i].style.display = 'none';
}
}
JSFiddle to try it out without modal functionality.
This also helps you if you maybe want to reset the list. To do so, just set the display property on every element to block.
Also, I don't knwo if it is a copy paste issue but the a at english breakfast is missing a < in the closing tag.

Is there a way to uniquely identity a dynamically added element?

I am trying to dynamically load a bunch of posts from a API and then implement a like button for each of them.
function load_allposts(){
fetch("/posts")
.then(response => response.json())
.then(posts => {
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=${element[0].author_id}> ${element[0].author_name} </button>
</div>
<div class="post-body">
${element[0].body}
</div>
<div class="p1">
<span class="like-status">${element[0].likes}</span> people like this
<button class="like-btn">${element[1]}</button>
</div>
<div class="post-time">
${element[0].timestamp}
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
});
}
I would to like to modify the <span class="like-status"> element when I click the <button class="like-btn">. The only way that I can think of to get a reference to <span class="like-status"> is by adding a ID to it by implementing some kind of counter, which I feel is more like a hack rather than real solution.
I tried googling but almost all solutions involved JQuery, which I am not familiar with. Any help would be appreciated.
You can use delegate event binding document.addEventListener('click', function(event) { to trigger click event for dynamically added button.
It will raise click on every element inside document you need to find if it is one which you expect with event.target.matches('button.like-btn').
Then you can find your span with getting parent and then finding span.like-status using querySelector.
Try it below. For demo modified load_allposts. You do not need to do any change in it.
load_allposts();
document.addEventListener('click', function(event) {
if (event.target.matches('button.like-btn')) {
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
});
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
}
<div id='all-posts'>
</div>
Note event delegation have extra overhead so alternatively you can use below code.
Here added two functions added as below and added one line bindClickEvent(enc); at end of load_allposts function.
likeClick - perform custom logic to update span.like-status
bindClickEvent - bind click event to all button.like-btn inside div
Call bindClickEvent(enc); at end of load_allposts function.
Try it below.
load_allposts();
// perform custom logic to update span.like-status
function likeClick(event) {
// querySelector will return first matching element
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
// bind click event to all button.like-btn inside div
function bindClickEvent(enc) {
// querySelectorAll will return array of all matching elements
let buttons = enc.querySelectorAll('button.like-btn');
// loop over each button and assign click function
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = likeClick;
}
}
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
// assign click event to buttons inside enc div.
bindClickEvent(enc);
}
<div id='all-posts'>
</div>

How to pass current ID to data delete react?

I have this code
var html = [];
for (var i = 0, len = this.props.tables.length; i < len; i++) {
var id = this.props.tables[i]._id;//._str;
html.push(
<div key={id} className="col-xs-6 col-md-3">
<div className={"thumbnail " + cls}>
<div>
<a role="button" className="btn btn-danger glyphicon glyphicon-trash"
onClick={() => { this.deleteTable(id) } } />
</div>
</div>
</div>);
}
which renders a list of "tables", and I am calling a function that removed the data given the id. However, mt ID I am passing ends up deleting a different "table" (the last one, always) so clearly I need to somehow save the id with the delete button somehow. How do I do that?
(I used to pass this.props.tables[i]._id but it told me that this.props.tables[i] is undefined because it was looking at the latest i),
This is React with Meteor.
I got around it by creating a new class TableDelete and passing the ID as a param.
So instead of the <a\> I have
<TableDelete key="del" params={this.props.tables[i]._id} />
where TableDelete is basically
/**
* Delete a row from the Tables collection, given its id
* #param id the id of the column
*/
deleteTable(id) {
Meteor.call('tables.removeTable', id);
}
/**
* Display the delete button based on the parameters
*/
render() {
// set the callback depending on the type of delete (row/col)
var callback = this.deleteTable; //row
var title = 'Delete Table';
// display the button
return (
<a role="button" data-toggle="tooltip" title={title}
className={'btn btn-danger glyphicon glyphicon-trash'}
onClick={() => { callback(this.props.params); } } >
</a>
);
}
and the params are
/**
* params: parameters to callback function
*/
TableDelete.propTypes = {
params: PropTypes.object.isRequired
};
I still do not know why this would work but the thing above wouldn't...

Add entries from the top in to-do list angular

I have made this to-do list in angular but would like the posts entered to entered fro m the top instead the bottom.
my code:
HTML
<a href="{{url.title}}" class="link">
<p class="title">{{url.name}}</p>
<p class="url">{{url.title}}</p>
</a>
</div>
<div class="col-md-4 delete m-b-2">
<!--a href="javascript:" ng-click="edit($index)" type="button" class="btn btn-primary btn-sm">Edit</a-->
Delete
</div>
</div>
</li>
JS
var urlFire = angular.module("UrlFire", ["firebase"]);
function MainController($scope, $firebase) {
$scope.favUrls = $firebase(new Firebase('https://lllapp.firebaseio.com/'));
$scope.urls = [];
$scope.favUrls.$on('value', function() {
$scope.urls = [];
var mvs = $scope.favUrls.$getIndex();
for (var i = 0; i < mvs.length; i++) {
$scope.urls.push({
name: $scope.favUrls[mvs[i]].name,
title: $scope.favUrls[mvs[i]].title,
key: mvs[i]
});
};
});
You can use unshift() instead of push() when you add elements to your array. It adds the element at the beginning of your array instead of at the end, and since your angular view is based on the model it will add it on top.
Use $scope.urls.splice(index_to_insert,0, object); so in your case you could do
var obj = {
name: $scope.favUrls[mvs[i]].name,
title: $scope.favUrls[mvs[i]].title,
key: mvs[i]
};
$scope.urls.splice(0,0, obj);

Categories