Attach event to dynamically created elements in javascript - javascript

I'm trying to insert HTML data dynamically to a table that is dynamically created, but when I try to attach an addEventListner for the button that is dynamically created the event is not firing. The solution would be appreciated.
const put = document.querySelector(".puthere");
const v1 = document.querySelector(".v1");
const v2 = document.querySelector(".v2");
const create = document.querySelector(".create");
const updateDelete = document.querySelector(".update-delete");
const update = document.querySelectorAll(".active-update");
const dynamic = document.querySelector(".dynamic");
//FUNCTIONS
const operations = function (v1, v2) {
const html = ` <tr class = "update-delete">
<td> <input type="${v1}"></td>
<td ><input type="${v2}"></td>
<td> <button class = "active-update" data-set=".active-update"> UPDATE</button></td>
<td> <button > ❌ </button></td>
</tr>`;
put.insertAdjacentHTML("beforeend", html);
};
//CREATE TABLE COLUMNS
create.addEventListener("click", function (e) {
operations(v1.value, v2.value);
});
//Ataching eventlistner to parent element
updateDelete.addEventListener("click", function (e) {
if(e.target.classList.contains('active-update'){
console.log('click)})
});
<div >
<table class = "puthere">
<tr class="dynamic">
<th>BOOKS</th>
<th class = "books">PRICE</th>
<th > <button class="create" > CREATE</button> </th>
</tr>
<tr class = "update-delete dynamic">
<td><input type="text" class="v1"></td>
<td><input type="text" class="v2"></td>
<td> <button class = "active-update" data-set=".active-update"> UPDATE </button></td>
<td> <button > ❌ </button></td>
</tr>
</table>
</div>

You should notice that you get updateDelete before you create another dynamic part. You could log updateDelete and you will find that it nerver changed.
So you could bind a click listener to higher level element, like this:
put.addEventListener('click', function (e) {
if (e.target.classList.contains('active-update')) {
console.log('click')
}
})
Or add click listener to every button you created dynamically.

Related

Delete row using inout field

I am creating table, and want to remove row by id using input field. (if input field matches with id then the row must be deleted)
can not figure it out.
Your help is much appreciated
`
<body onload="addRow()">
<table id="myTable" style="display:none">
<tr>
<th class="borderless">ID</th>
<th>Name</th>
<th>Username</th>
<th>Gender</th>
<th>Country</th>
</tr>
<tbody id="myTableBody">
</tbody>
</table>
<button type="button" id="buttonShow" onclick="showTable()">Show Table</button>
<button type="button" id="buttonAdd" onclick="addRow()" disabled>Add a new row</button>
<br>
<label>
<input class="input1" type="text" name="todoTags"/>
<button class="dellbtn" id="buttonDell"onclick="delRow()" disabled>Delete row</button>
</label>
`
`
function showTable(){
document.getElementById("myTable").style.display = "block";
document.getElementById("buttonAdd").disabled = false;
document.getElementById("buttonDell").disabled = false;
}
const btn = document.querySelector('.dellbtn')
const userTags = []
`
Here is my: JSfiddle
What you could do is changing the addRow() method so it adds a data-attribute to each row, in the <tr>. You can achieve this goal by adding this when creating the row :
row.setAttribute("row-id", tr.length - 1);
Then, when you want to delete it, you can simply search the
row that has the data-attribute that you just input. And it will look like this :
function delRow() {
const value = document.getElementById("valueToDelete").value;
document.querySelector('[row-id="' + value + '"]').remove();
}
I created a fork to your JSFiddle that you can check right here.
Hope it helps ! Good luck :)
Not exactly what you're asking, but this method might work better for you. It uses a delete button for each row so you can decide which one to delete. Then it uses a delegate listener to enable the delete buttons
const table = document.querySelector('#theTable tbody');
let c = 1
const addRow = () => {
table.innerHTML += `<tr><td>${c} data</td><td>${c} data</td><td><button class='delete'>delete</button></td></tr>`;
c++
}
// delegate listener for the delete button
table.addEventListener('click', e => {
if (e.target.classList.contains('delete')) {
e.target.closest('tr').remove();
}
})
<table id='theTable'>
<th>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td></td>
</tr>
</th>
<tbody>
</tbody>
</table>
<button onclick='addRow()'>Add Row</button>

How do I get and pass the field of the row having class="name" in the following html?

<tbody>
<tr>
<td>gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button onclick="fetchdata(parameter)">Fetch Details</button>
</td>
</tr>
</tbody>
In the above html, I want that the function fetchdata('parameter') to contain the text content of the td which has a class of name and is hidden, as the parameter.
OR
I need a way in which I can get the text content of the td having class of name in my javascript function.
i.e.
function fetchdata() {
const name = document.somethingThatGivesMeName()
}
NOTE: There are going to be multiple rows that I may require to get the name of so I can't directly do document.queryselector('.name')
Sorry, This might be pretty simple but I can't quite figure it out.
When clicking the button find the first row up in the tree relative to the button with the closest method. Then from the row select the element with the class name and read the textContent or innerText of that element.
const buttons = document.querySelectorAll('.js-fetch-details');
function fetchDetails(event) {
const row = event.target.closest('tr');
const name = row.querySelector('.name').textContent;
console.log(name);
}
buttons.forEach(button => button.addEventListener('click', fetchDetails));
<table>
<tbody>
<tr>
<td>gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button class="js-fetch-details">Fetch Details</button>
</td>
</tr>
</tbody>
</table>
You just need the quotes ':
function fetchdata(value){
console.log(value)
}
<tbody>
<tr>
<td>gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button onclick="fetchdata('parameter')">Fetch Details</button>
</td>
</tr>
</tbody>
or you can use event listener and data value:
document.querySelectorAll('button').forEach(el => {
el.addEventListener('click', e => {
e = e || window.event;
e = e.target || e.srcElement;
console.log(e.dataset.value)
})
})
<tbody>
<tr>
<td>gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button data-value="parameter">Fetch Details</button>
</td>
</tr>
</tbody>
You can use document.getElementsByClassName('name')
This will get all the elements that have class of name.
I would put the listener on the <tbody> instead.
document.querySelector('tbody').addEventListener('click', (e) => {
// Clicking on the whole row
if (e.target.nodeName === 'TR') {
const name = e.target.querySelector('.name').textContent;
console.log(name);
}
// Clicking on the button
// Give the button a class
if (e.target.classList.contains('.somebuttonclass')) {
const name = e.target.parentNode.parentNode.querySelector('.name').textContent;
console.log(name);
}
});
UPDATE
closest would also work
document.querySelector('tbody').addEventListener('click', (e) => {
// Clicking on the whole row
if (e.target.nodeName === 'TR') {
const name = e.target.querySelector('.name').textContent;
console.log(name);
}
// Clicking on the button
// Give the button a class
if (e.target.classList.contains('.somebuttonclass')) {
const name = e.target.closest('tr').querySelector('.name').textContent;
console.log(name);
}
});
First you get all elements with class="name", then you pick just (the first) one with the attribute "hidden".
It's a way to do it anyway.
function fetchdata() {
const tds = document.getElementsByClassName("name")
for(let i = 0; i < tds.length; i++){
if(tds[i].getAttribute("hidden") != null) {
console.log(tds[i].innerHTML)
}
}
}
<table>
<tr>
<td class="name">gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td class="name">1</td>
<td>
<button onclick="fetchdata()">Fetch Details</button>
</td>
</tr>
</table>
With jQuery you can just do:
function fetchdata() {
console.log($('.name[hidden]').html());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td>gibberish</td>
<td class="name" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button onclick="fetchdata()">Fetch Details</button>
</td>
</tr>
</table>
Note that you need to have a table around your structure for any of this to work properly. You can't have tbody, tr and td outside a table.
If you use document.getElementsByClassName you will get what you want.
However, if there will be a case where more than one instance of that class name will occur, then you need to iterate through the classes and get their values.
The following should solve your problem
<html>
<head>
<script>
function fetchdata(){
var data = document.getElementsByClassName("data");
var t = data.length;
for(i = 0; i< t; i++){
var content = data[i].innerHTML;
alert (content);
}
}
</script>
<body>
<table>
<tbody>
<tr>
<td>gibberish</td>
<td class="data" hidden>200398</td>
<td>iPhone X 64Gb Grey</td>
<td>$999.00</td>
<td>1</td>
<td>
<button onclick="fetchdata()">Fetch Details</button>
</td>
</tr>
</tbody>
</table>
</body>
</html>

Append search results to respective parent div?

I'm implementing autocomplete using webSocket. When I type in the input field which has the classname item-search-results, a bunch of result will show up search results screenshot. My problem is the result will is only attach to the very first item-search-results input field.
But I need the results show up under the right item-search-results input field.
For example, I type on the 4th item-search-results input field, but the results are attach to the first one results are attach to the first row screenshot
Here is my webSocket js, I use jQuery:
$(function () {
let webSocket = new WebSocket("ws://localhost:8000/autocomplete");
webSocket.onopen = (msgEvent) => {
console.log('connected to WebSocket!');
};
webSocket.onmessage = (msgEvent) => {
/* results shows {"results":{"results":[{..}]}}.
results.results shows {results:[{..}]}
results.results.results shows [{...}], which is an array */
let results = JSON.parse(msgEvent.data),
resultsArr = results.results.results;
console.log(resultsArr);
$.each(resultsArr, (index, value) => {
$('<div>' + value['name'] + '</div>').attr('id', 'item-search-results-' + index).appendTo($('.item-search-results'));
});
};
webSocket.onclose = (msgEvent) => {
console.log('disconnected');
};
webSocket.onerror = (msgEvent) => {
console.log('there\'s an error');
};
/**
* Request to display data
*/
$('.add-items-table').on('input', '.item-input', (e) => {
let value = e.target.value;
webSocket.send(value);
console.log(value);
})
})
Websockets are irrelevant here, and I don't understand why you are doing an append inside loop. When trying to do something like this, you need to use the $(this) selector which will be the element you actually click.
At the top of your script create a global variable or namespace.
var Item = {};
/* In this chunk of code, create an object which you can use as a reference */
$('.add-items-table').on('input', '.item-input', (e) => {
/* this will be the correct .item-search-results div */
var Item.list = $(this).siblings('.item-search-results');
let value = e.target.value;
webSocket.send(value);
console.log(value);
})
Finally, append to the correct <div>.
.appendTo(Item.list);
Not sure about Websocket But here is how you will approach.Get last Row of table and append data after that particular row.
var placeHolder=` <tr>
<td style="width: 16.66%">
<input type="text" name="quanty" value="">
</td>
<td class="item-search-container">
<input class="item-input" id="item-input-0" type="search" name="item" value="">
<div class="item-search-results"></div>
</td>
<td>
<input type="text" name="quanty" value="">
</td>
</tr>`;
function addItem(){
let totalElem=document.getElementsByClassName('tr').length;
let lastElem=document.getElementsByClassName('tr')[totalElem-1];
$.get( "https://jsonplaceholder.typicode.com/posts/1", function( data ) {
$.each(data, function(i, item) {
$(lastElem).after(placeHolder);
});
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="item-form">
<table class="add-items-table table table-hover ">
<thead>
<tr>
<th>Quanty</th>
<th>Item</th>
<th>Comment</th>
<th>Remove</th>
</tr>
</thead>
<tbody class="item-row">
<tr class="tr">
<td style="width: 16.66%">
<input type="text" name="quanty" value="">
</td>
<td class="item-search-container">
<input class="item-input" id="item-input-0" type="search" name="item" value="">
<div class="item-search-results"></div>
</td>
<td>
<input type="text" name="quanty" value="">
</td>
<td>
<button class="remove-item-button btn-block btn-outline-danger" type="reset" name="remove">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
</td>
</tr>
</tbody>
</table>
<button class="add-item" onClick="addItem()">Add Another Item</button>
</section>
The problem here is that you are appending it to $('.item-search-results') that is a class and is returned as an array. When you run .appendTo($('.item-search-results')) it will append to the first item of the array.
You should use a more specific Id for each roll and append by Id.
I think #manikant 's idea of creating a template is great, but I would introduce a variable that you can change for each new row you create and give a different Id to each .item-search-results div. But you would have to send this to the websocket and receive it back to call the right Id to append.

querySelector inside of Table

I have this simple chrome extension that allows the user to create a table however I can't bind the remove Button.
options.html
<body>
<!-- would be created as the user clicks on new Row -->
<table id="rows">
<tr id="row0">
<td>
<input id="textBox" type="text">
<button id="remove">Remove</button>
</td>
</tr>
</table>
<!-- used as template -->
<table hidden>
<tbody>
<tr id="rowTemplate">
<td>
<input id="textBox" type="text">
<button id="remove">Remove</button>
</td>
</tr>
</tbody>
</table>
<button id="newRow">new Row</button>
<script src="options.js"></script>
options.js
function Row() {
var rows = document.getElementById('rows');
this.node = document.getElementById('rowTemplate').cloneNode(true);
this.node.id = 'row' + (Row.next_id++);
this.node.row = this;
rows.appendChild(this.node);
this.node.hidden = false;
var row = this;
this.getElement('remove').onclick = function () {
row.node.parentNode.removeChild(row.node);
}
}
Row.next_id = 0;
Row.prototype.getElement = function (name) {
return document.querySelector('#' + this.node.id + ' .' + name);
}
window.onload = function() {
document.getElementById('newRow').onclick = function() {
new Row();
};
}
The problem lies in the querySelector because whenever I click on 'new Row', I get an error Uncaught TypeError: Cannot set property 'onclick' of null pointing to the getElement('remove').onclick which calls getElement() where the querySelector returns null.

Iterating through table and get the value of a button in each tablerow jQuery

I have buttons in a table which are created dynamically. I want to iterate through a table, get the tablerows which contain a checked checkbox and get the value of a button inside the tablerow. I want to push the values in an array after. The buttons don't have a unique ID so I cannot get their values by id.
I tried to get the values through giving the buttons a class and itering works fine but the array is filled with empty entries.
$("#bt_multiple_deletion").off().on("click", function () {
var files = [];
var rows = $(".select").find("input[type=checkbox]:checked");
rows.each(function () {
files.push($(this).find(".filefolder-button").text());
});
})
I really don't know what Im doing wrong. I tried to get the values with .text(), .val() etc.
My table row looks like this:
<tr class="select">
<td>
<span class="countEntries"><input id="lv_fifo_ctrl7_cb_delete_file" type="checkbox" name="lv_fifo$ctrl7$cb_delete_file" /></span>
</td>
<td>
<img src="images/icons/013_document_02_rgb.png" alt="document" />
</td>
<td class="name">//the button i want to get the value from
<input type="submit" name="lv_fifo$ctrl7$bt_file" value="013_document_png.zip" id="lv_fifo_ctrl7_bt_file" class="filefolder-button download file del" style="vertical-align: central" />
</td>
<td>
<span id="lv_fifo_ctrl7_lb_length">33.14 KB</span>
</td>
<td>
<span id="lv_fifo_ctrl7_lb_CreationTime">21.10.2014 07:34:46</span>
</td>
<td></td>
<td>
<input type="submit" name="lv_fifo$ctrl7$bt_del_file" value="delete" id="lv_fifo_ctrl7_bt_del_file" class="delete-button delete-file" />
</td>
</tr>
The problem is rows is the input elements not the tr elements so in the loop you need to find the tr which contains the input then find the target element inside it
$("#bt_multiple_deletion").off().on("click", function () {
var checked = $(".select").find("input[type=checkbox]:checked");
var files = checked.map(function () {
return $(this).closest('tr').find(".filefolder-button").val();
}).get();
})
Another option is
$("#bt_multiple_deletion").off().on("click", function () {
var rows = $(".select").find("tr").has('input[type=checkbox]:checked');
//var rows = $(".select").find('input[type=checkbox]:checked').closest('tr');
var files = rows.map(function () {
return $(this).find(".filefolder-button").val();
}).get();
})
#Timo Jokinen Do you need this
$("#bt_multiple_deletion").on("click", function () {
var files = [];
var rows = $(".select").find("input[type=checkbox]:checked");
rows.each(function () {
files.push($(this).parents("tr").find("td.filefolder-button").text());
});
console.log(files);
})
<table class="select">
<tr>
<td class="filefolder-button">test1</td>
<td><input type="checkbox" /></td>
</tr>
<tr>
<td class="filefolder-button">test2</td>
<td><input type="checkbox" /></td>
</tr>
<tr>
<td class="filefolder-button">test3</td>
<td><input type="checkbox" /></td>
</tr>
</table>
<button id="bt_multiple_deletion">delete</button>
Checkout example link here

Categories

Resources