Checkbox click overriding table row click - javascript

I have checkbox inside HTML table and I set onclick event on the HTML table row.
When I click the table row, it will fire a function on my script
<table>
<tr onclick="sayHello('Hello World');">
<td><input type="checkbox" /></td>
<td>Column 1</td>
<td>Column 2</td>
<td>Column 3</td>
</tr>
</table>
The problem is when I click a checkbox inside that row, it also will fire the row's onclick event
How to prevent that?

You can simply add onclick event of checkbox to call event.stopPropagation()
<input type="checkbox" onclick="event.stopPropagation();" />
Ref: https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
Good to read one is
http://javascript.info/tutorial/bubbling-and-capturing

var checkboxes = document.querySelectorAll("tr input");
for (var i = 0, l = checkboxes.length; i < l; i++) {
checkboxes[i].onclick = function(e) {
e.stopPropagation();
}
}
<table>
<tr onclick="alert('Hello World');">
<td><input type="checkbox" /></td>
<td>Column 1</td>
<td>Column 2</td>
<td>Column 3</td>
</tr>
</table>
Stop the event from propagating to the table:
var checkboxes = document.querySelectorAll("tr input");
for (var i = 0, l = checkboxes.length; i < l; i++) {
checkboxes[i].onclick = function(e) {
e.stopPropagation();
}
}

You can try like this:
http://jsfiddle.net/5jnfzy7o/
var c=document.getElementById('something')
c.addEventListener('click', function(){
event.stopPropagation(); //Stops event from bubbling up the DOM Tree
});
function sayHello(str){
alert(str);
}
For HTML :
<table>
<tr onclick="sayHello('Hello World');">
<td><input type="checkbox" id="something"/></td>
<td>Column 1</td>
<td>Column 2</td>
<td>Column 3</td>
</tr>
</table>

You just faced with event bubbling :)
You have to stop the propagation using stopPropagation event's method. See an example here.

You need to stop the propagation of the click event.
document.querySelector('input').addEventListener('click', function(evt) {
evt.stopPropagation()
})
You can start from the MDN documentation about stopPropagation and read on event flow to understand more about this.

sayHello = function(whatToSay)
{
alert(whatToSay);
}
<table>
<tr onclick="sayHello('Hello World');">
<td><input type="checkbox" onclick="event.stopPropagation();" /></td>
<td>Column 1</td>
<td>Column 2</td>
<td>Column 3</td>
</tr>
</table>

Related

Prevent parent click being fired when dragging outside child editable element

Let's say I have a table where each row have a onClick event.
Inside each row, one of the cell have an input.
function handleRowClick() {
console.log('click on row')
}
function handleCellClick(e) {
e.stopPropagation()
console.log('click on cell')
}
<table>
<tr onclick="handleRowClick(event)">
<td>COLUMN 1</td>
<td onclick="handleCellClick(event)">
<input value="random string" />
</td>
<td>COLUMN 3</td>
</tr>
</table>
How to prevent handleRowClick being fired when I'm dragging inside the input to select all the text if my mouse end on one of the adjacent cell?
You could use a flag to signify if the mouse down occurred on the input element, and prevent processing in handleRowClick()
var flag;
var inp = document.querySelectorAll("input")[0];
var row = document.querySelectorAll("tr")[0];
inp.addEventListener('mousedown', function (e) {
console.log("input mousedown");
flag = true;
e.stopPropagation();
});
row.addEventListener('mousedown', function (e) {
flag = false; //in case the mouse was released on the body
});
function handleRowClick() {
if (flag) {
flag = false;
return;
}
console.log('click on row');
}
function handleCellClick(e) {
e.stopPropagation();
console.log('click on cell');
}
<table>
<tr onclick="handleRowClick(event)">
<td>COLUMN 1</td>
<td onclick="handleCellClick(event)">
<input value="random string" />
</td>
<td>COLUMN 3</td>
</tr>
</table>

Toggle elements from classes with JS

I would like to show more information when someone click on show more buttons. The complication is that there are several buttons and informations to toggle with same className.
What am I doing wrong??
var element = document.querySelectorAll("btn");
for (var i = 0; i < button_length ; i++) {
element[i].addEventListener("click", function() {
alert("Button Clicked " + i);
element[i].classList.toggle("extrainfo");
};
}
td{border:solid 1px black;}
.btn, #btn_id{color:blue; text-decoration:underline; cursor:pointer;}
.extrainfo{
display:none
}
<table>
<tbody>
<tr class="info_group">
<td>Title 1</td>
<td class="btn">show more</td>
</tr>
<tr>
<td class="extrainfo" colspan="2">More info 1</td>
</tr>
</tbody>
<tbody>
<tr class="info_group">
<td>Title 2</td>
<td class="btn">show more</td>
</tr>
<tr>
<td class="extrainfo" colspan="2">More info 2</td>
</tr>
</tbody>
<tbody>
<tr class="info_group">
<td>Title 3</td>
<td class="btn">show more</td>
</tr>
<tr>
<td class="extrainfo" colspan="2">More info 3</td>
</tr>
</tbody>
</table>
to work you javaScript
var element = document.querySelectorAll("btn"); // need to be (".btn")
// you want it to be i < element.length; ? or there's a variable called button_length?
for (var i = 0; i < button_length ; i++) {
element[i].addEventListener("click", function() {
alert("Button Clicked " + i);
element[i].classList.toggle("extrainfo");
}; // missing a Parenthesis need to be this }); not this };
}
I'm still not sure about the functionality, but see the code below if that's what you're looking for.
var element = document.querySelectorAll(".btn");
var extraInfo = document.querySelectorAll(".extrainfo");
for (let i = 0; i < element.length; i++) {
element[i].addEventListener("click" , function() {
extraInfo[i].classList.toggle("extrainfo");
});
}
hereJSFiddle you can play around with the code

Populate a JS array with all values in a table row

I've hardly used javascript and I'm stuck:
I've got a table with id JamTable
I'm trying to write some JS that will get me an array of each <td> value for any row clicked on, so that I can present it in a popup, wing it back to the server via POST request using an ajax call and then update the elements on the table so no postback is required - but so far I can't even get an array populated.
I've got:
$(document).ready(function () {
// Get all table row elements <tr> in table 'JamTable' into var 'tr'
var tr = $('#JamTable').find('tr');
// Bind a 'click' event for each of those <tr> row elements
tr.bind('click', function (e) {
// so that when a row is clicked:
var r = $(this).closest('tr').row;
var myArray = new Array(r.cells);
for (var c = 0, col; col = r.cells[c]; c++) {
alert(col.text)
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="JamTable">
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>
Yeah I'm totally lost when it comes to JS
Using proper event-delegation is key to success in such scenarios. Catching "click" events on rows is guaranteed to work, even with dynamically-added rows (which were added to the DOM after the event listener was defined)
Breakdown (see comments):
const tableElm = document.querySelector('table')
// listen to "click" event anywhere on the <table>
tableElm.addEventListener('click', onTableClick)
function onTableClick(e){
// event delegation
const rowElm = e.target.closest('tr')
// traverse each child of the row (HTMLCollection). map the text value into an Array
// https://stackoverflow.com/a/34250397/104380
const values = rowElm ? [...rowElm.children].map(td => td.innerText) : []
// print result
console.clear()
console.log( values )
}
<table>
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>
You should probably also have some unique id on the <tr> if you are sending data back to the server, it might need to know to which row it belongs to
You can delegate the event from tr. On click of it get the children. Using Array.from will create an array of td. Using map to iterate that and get the text from the td
$("#JamTable").on('click', 'tr', function(e) {
let k = Array.from($(this).children()).map((item) => {
return item.innerHTML;
})
console.log(k)
})
td {
border: 1px solid green;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id='JamTable'>
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>
You don't need jquery for that.
You may use querySelectorAll to get the trs and simply children on the tr node to get the tds
const trs = [...document.querySelectorAll('tr')]
trs.forEach(tr => tr.addEventListener('click', e => {
// whenever it is clicked, get its tds
const values = [...tr.children].map(td => td.innerText)
console.log('click', values)
}, false))
<table>
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>
As #vsync suggested, better to use event delegation in case you have a lot of rows to avoid binding several clicks. This also allows to add more rows later on without to have to bind more click handler
edit2 still thx to #vsync, avoid using onclick and prefer addEventListener to avoid overriding existing events
const table = document.querySelector('table')
table.addEventListener('click', e => {
if (e.target.nodeName !== 'TD') { return }
const values = [...e.target.parentNode.children].map(c => c.innerText)
console.log(values)
}, false)
<table>
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>
$('#JamTable tbody tr').click(function () {
var arr = [];
$($(this).children('td')).each(function (index, val) {
arr.push(val.innerText);
});
console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="JamTable">
<tbody>
<tr>
<td>1</td>
<td>JAM</td>
<td>0.004</td>
</tr>
<tr>
<td>3</td>
<td>BOB</td>
<td>0.24</td>
</tr>
<tr>
<td>9</td>
<td>Nasty Simon</td>
<td>94.3</td>
</tr>
</tbody>
</table>

Show value in textarea if checkbox is true

I have a table with four columns Name, Age, Country and a checkbox. If the checkbox is clicked(true) the name value of the row is showed in a textarea.
I am not really sure how I can realise that.
A row:
<tr>
<th scope="row">1</th>
<td>James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr>
if the checkbox is true the value "James" should be shown in a textarea.
Thank you all.
I believe you need to do this for multiple rows in a table.
First select all the checkboxes with Document.querySelectorAll() to attach the event (click) to all the checkboxes.
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.
Inside the event (click) handler function target all the checked checkboxes to loop through them to get the relevant names using Array.prototype.map():
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Also it is not good practice to mix up th and td inside of the same tr element. You should place th inside of a thead and td inside of tbody element:
var cb = document.querySelectorAll('input[type=checkbox]');
cb.forEach(function(ck){
ck.addEventListener('click', function(el){
var checked = document.querySelectorAll(':checked');
var tArea = document.getElementById('myText');
tArea.value = Array.from(checked).map(c => c.closest('tr').querySelector('td:nth-child(2)').textContent);
//or using spread syntax
//tArea.value = [...checked].map(c => c.closest('tr').querySelector('td:nth-child(2)').textContent);
});
});
table, th, td {
border: 1px solid black;
}
<table>
<thead>
<tr>
<th>Sl</th>
<th>Name</th>
<th>No</th>
<th>Country</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">1</td>
<td>James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr>
<tr>
<td scope="row">2</td>
<td>John</td>
<td>15</td>
<td>Germany</td>
<td><input type="checkbox"></td>
</tr>
</tbody>
</table>
<textarea id="myText"></textarea>
Try this:
[...document.querySelectorAll("input[type='checkbox']")].forEach(function (v){
v.addEventListener("change", function(){
document.querySelector("textarea").value = (this).checked ? (this).parentElement.parentElement.querySelector("td").innerHTML:'';
})});
<table>
<tr>
<th scope="row">1</th>
<td>James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr>
<tr>
<th scope="row">1</th>
<td>James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr>
</table>
<textarea></textarea>
Few javascript lines would be ok for you need.
let input = document.querySelector('input');
input.addEventListener('change', e=> {
if(e.target.checked == true){
// retreive tags
let name = document.querySelector('.name');
let textarea = document.querySelector('.textarea');
// insert data
textarea.innerText += name.innerText
}
})
<table>
<tr>
<th scope="row">1</th>
<td class="name">James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr>
</table>
<textarea class="textarea"></textarea>
Since your table can have multiple rows, where each can have the checkbox set or not, the textarea could get zero, one or more names.
So you would need to listen the change event, and then iterate the rows to collect the names, to finally set the value of the textarea:
let textarea = document.querySelector("textarea");
let table = document.querySelector("table");
table.addEventListener("change", function (e) {
let names = [];
for (let row of table.rows) {
if (row.querySelector("input[type=checkbox]").checked) {
names.push( row.children[1].textContent);
}
}
textarea.value = names.join("\n");
});
<table><tr>
<th scope="row">1</th>
<td>James</td>
<td>13</td>
<td>France</td>
<td><input type="checkbox"></td>
</tr><tr>
<th scope="row">2</th>
<td>Lucy</td>
<td>14</td>
<td>Germany</td>
<td><input type="checkbox"></td>
</tr></table>
<textarea></textarea>

Get ID of Table that contains a checkbox

I have many tables each one with an ID, (table1,2,3,...), and in each one I have many TD's <td><a href
example :
<table id="myTable1" class="someclass">
<tbody>
<tr>
<td>blablabla</td>
<td>random text</td>
<td>randomtext</td>
</tr>
</tbody>
</table>
</td>
<table id="myTable2" class="someclasse">
<tbody>
<tr>
<td>blablabla</td>
<td>random text</td>
<td>randomtext</td>
</tr>
</tbody>
</table>
</td>
(don't look at the HTML code it's not important for now )
My goal is to open all hrefs within the table "table X" then open them in new tab. I do that with
var els = document.getElementById("myTable1").querySelectorAll("a[href^='https://domaine.']");
for (var i = 0, l = els.length; i < l; i++) {
var el = els[i];
alert(el)
window.open (el,"_blank");
}
It works like a charm. Now I want to add a checkbox to each table, and if checked to open the href on "the" table I checked (I did some innerHTML to "insert" checkbox). Now my question, how can I get the table ID when I'll check the checkbox?
For example I check the table that have "table6" and then every link in that table gets opened.
table id=1 (checkbox)
table id=2 (checkbox)
etc
if i check the checkbox it will get the table with id 2
You can use closest to get the closest table, then you can get the id from that.
// List of checkboxes
let inputs = Array.from(document.querySelectorAll('input[type=checkbox]'))
// Add a click event to each
inputs.forEach(input => {
input.addEventListener('click', e => {
let target = e.currentTarget
// If the checkbox isn't checked end the event
if (!target.checked) return
// Get the table and id
let table = target.closest('table')
let id = table.id
console.log(id)
})
})
<table id="abc">
<tr>
<td><input type="checkbox"></td>
</tr>
</table>
<table id="def">
<tr>
<td><input type="checkbox"></td>
</tr>
</table>
<table id="ghi">
<tr>
<td><input type="checkbox"></td>
</tr>
</table>
<table id="jkl">
<tr>
<td><input type="checkbox"></td>
</tr>
</table>
You say that you are adding the checkbox dynamically, so you won't want to do a querySelectorAll like I did above. You will want to add it when it is created like this:
// List of tables
let tables = Array.from(document.querySelectorAll('table'))
// insert the checkbox dynamically
tables.forEach(table => {
table.innerHTML = '<tr><td><input type="checkbox"></td></tr>'
// Get the checkbox
let checkbox = table.querySelector('input[type=checkbox]')
// Add an eventlistener to the checkbox
checkbox.addEventListener('click', click)
})
function click(e) {
let target = e.currentTarget
// If the checkbox isn't checked end the event
if (!target.checked) return
// Get the table and id
let table = target.closest('table')
let id = table.id
console.log(id)
}
<table id="abc">
</table>
<table id="def">
</table>
<table id="ghi">
</table>
<table id="jkl">
</table>
…I want to add a checkbox to each table, and if [it's] checked…open the href [in] "the" table I checked…how can I get the table ID when I'll check the checkbox?
Given that you want to find the id of the <table> within which the check-box <input> is contained in order to select the <table> via its id property you don't need the id; you simply need to find the correct <table>.
To that end I'd suggest placing an event-listener on each of those <table> elements, and opening the relevant links found within. For example (bearing in mind that there are restrictions on opening new windows/tabs on Stack Overflow, I'll simply style the relevant <a> elements rather than opening them):
function highlight(e) {
// here we find the Static NodeList of <a> elements
// contained within the <table> element (the 'this'
// passed from EventTarget.addEventListener()) and
// convert that Array-like collection to an Array
// with Array.from():
Array.from(this.querySelectorAll('a'))
// iterating over the Array of <a> elements using
// Array.prototype.forEach() along with an Arrow
// function:
.forEach(
// here we toggle the 'ifCheckboxChecked' class-name
// via the Element.classList API, adding the class-name
// if the Event.target (the changed check-box, derived
// from the event Object passed to the function from the
// EventTarget.addEventListener function) is checked:
link => link.classList.toggle('ifCheckboxChecked', e.target.checked)
);
}
// converting the Array-like Static NodeList returned
// from document.querySelectorAll() into an Array:
Array.from(document.querySelectorAll('table'))
// iterating over the Array of <table> elements:
.forEach(
// using an Arrow function to pass a reference to the
// current <table> element (from the Array of <table>
// elements to the anonymous function, in which we
// add an event-listener for the 'change' event and
// bind the named highlight() function as the event-
// handler for that event:
table => table.addEventListener('change', highlight)
);
function highlight(e) {
Array.from(this.querySelectorAll('a'))
.forEach(
link => link.classList.toggle('ifCheckboxChecked', e.target.checked)
);
}
Array.from(document.querySelectorAll('table')).forEach(
table => table.addEventListener('change', highlight)
);
body {
counter-reset: tableCount;
}
table {
width: 80%;
margin: 0 auto 1em auto;
border: 1px solid limegreen;
}
table::before {
counter-increment: tableCount;
content: 'table' counter(tableCount);
}
a.ifCheckboxChecked {
background-color: #f90;
}
<table>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
</tbody>
</table>
JS Fiddle demo.
References:
CSS:
::before pseudo-element
Using CSS Counters.
JavaScript:
Array.from().
Array.prototype.forEach().
Arrow Functions.
Element.querySelectorAll().
Event.
EventTarget.addEventListener().

Categories