Trouble with event bubbling and JavaScript checkbox - javascript

I'm trying to create an HTML table with checkboxes in its leftmost column. I want to be able to select the checkbox by clicking anywhere on the <tr> element. I've gotten it to work, but I when I click the checkbox itself it doesn't change state. I've tested this in Firefox 54 (I don't care about other browsers).
I've made a JSFiddle demonstrating my problem https://jsfiddle.net/a92to0tu/
let table = document.querySelector("table");
table.addEventListener("click", function(e) {
e.preventDefault();
let tr = e.target.closest("tr");
let checkbox = tr.firstElementChild.firstElementChild;
// This doesn't work
checkbox.checked = !checkbox.checked
// This works but I don't like it
// setTimeout(function() {
// checkbox.checked = !checkbox.checked
// }, 100);
});
<table>
<tr>
<td><input type="checkbox"></td>
<td>Click works here too</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Click works here too</td>
</tr>
</table>
<p>I can click the text/table row, but clicking the checkbox no longer works</p>

Use a label element, then you don't need any script at all.
table {border-collapse: collapse;}
td { border: 1px solid #999999;}
<table>
<tr><td><input type="checkbox" id="foo" name="foo">
<td><label for="foo">Works here too!</label>
<td><label for="foo">Works here three!</label>
</table>

You need to set a condition to make sure the click isn't targeting the checkbox:
if(e.target !== checkbox) {
let table = document.querySelector("table");
table.addEventListener("click", function(e) {
let tr = e.target.closest("tr");
let checkbox = tr.firstElementChild.firstElementChild;
if (e.target !== checkbox) {
checkbox.checked = !checkbox.checked
}
});
<table>
<tr>
<td><input type="checkbox"></td>
<td>Click works here too</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Click works here too</td>
</tr>
</table>
<p>I can click the text/table row, but clicking the checkbox no longer works</p>

Related

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().

Check/uncheck a checkbox in the table row when any checkbox in the same row is clicked

I have a simple table as following which has checkboxes in the first and last columns of each row.
<table style="width:100%">
<tr>
<td><input type="checkbox" /></td>
<td>Smith</td>
<td><input type="checkbox" /></td>
</tr>
<tr>
<td><input type="checkbox" /></td>
<td>Jackson</td>
<td><input type="checkbox" /></td>
</tr>
</table>
Problem:
When I check/uncheck the last column's checkbox in the first row, the first column's checkbox in the same row should be checked/unchecked. Similarly, if I check/uncheck the first column's checkbox, the corresponding last column checkbox should be checked/unchecked.
How can I achieve this in javascript? Any help or pointers would be really appreciated.
Here is the fiddle which I have created: Fiddle
Thank you.
Use :checkbox selector to select input type checkbox elements.
Try this:
$(':checkbox').on('change', function() {
$(this).closest('tr').find(':checkbox').prop('checked', this.checked);
});
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<table style="width:100%">
<tr>
<td>
<input type="checkbox" />
</td>
<td>Smith</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>Jackson</td>
<td>
<input type="checkbox" />
</td>
</tr>
</table>
Using JavaScript:
Use querySelectorAll('[type="checkbox"]') to find checkbox elements.
Try this:
var checkboxes = document.querySelectorAll('[type="checkbox"]');
[].forEach.call(checkboxes, function(checkbox) {
checkbox.onchange = function() {
var currentRow = this.parentNode.parentNode;
var cbElems = currentRow.querySelectorAll('[type="checkbox"]');
[].forEach.call(cbElems, function(cb) {
cb.checked = this.checked;
}.bind(this))
};
});
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
<table style="width:100%">
<tr>
<td>
<input type="checkbox" />
</td>
<td>Smith</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>Jackson</td>
<td>
<input type="checkbox" />
</td>
</tr>
</table>
One possible Javascript solution to toggle Checkboxes on Table Row click is shown below:
HTML
<table id = "Table1">
<tr>
<td><input type="checkbox" /></td>
<td>John Smith</td>
<td><input type="checkbox" /></td>
</tr>
<tr>
<td><input type="checkbox" /></td>
<td>Anna Warner</td>
<td><input type="checkbox" /></td>
</tr>
</table>
CSS
table, th, td{
border: 1px solid #c0c0c0;
border-collapse: collapse;
}
table{width:100%;}
Javascript
// row click will toggle checkboxes
row_OnClick("Table1")
function row_OnClick(tblId) {
try {
var rows = document.getElementById(tblId).rows;
for (i = 0; i < rows.length; i++) {
var _row = rows[i];
_row.onclick = null;
_row.onclick = function () {
return function () {selectRow(this);};
}(_row);
}
}
catch (err) { }
}
function selectRow(row) {
row.cells[0].firstChild.checked = !row.cells[0].firstChild.checked;
row.cells[2].firstChild.checked = row.cells[0].firstChild.checked;
}
Working jsfiddle demo at: https://jsfiddle.net/t6nsxgnz/
Practical implementation at: http://busny.net
You can further customize this solution pertinent to your task by modifying the selectRow(row) function:
function selectRow(row) {
row.cells[0].firstChild.checked = // add your code for the 1st CheckBox
row.cells[2].firstChild.checked = // add your code for the 2nd CheckBox
}
Another variation of this functionality coded in jQuery can be found in online pop-quiz engine (http://webinfocentral.com), implemented via the follwoing code snippet:
// toggle Checkboxes on row click
$(Table1 tr').click(function (event) {
// find the checkbox in the row
var _chk = $(this).find('input:checkbox');
if (!($(event.target).is("checkbox"))) {
$(_chk).prop('checked', !$(_chk).prop('checked'));
}
});
In this case, Row Click (at any place of the Row) or CheckBox Click events will toggle the state of that particular CheckBox. The state of other CheckBoxes can be synchronized with this one (by using "siblings" property, for example).
Hope this may help.

jQuery: How to check if certain checkboxes are checked, and change styles accordingly?

I've been trying to figure this out, but am not sure in which way to approach it using jQuery. I have a table of classes, and according to the classes that are checked, I want to change the background of tbody to reflect that this requirements are met.
<tbody>
<tr class="active header">
<th colspan="5"><b>Math (3 Courses)</b></th>
</tr>
<tr>
<td>MAC2311</td>
<td>Calculus I w/ Analytic Geometry</td>
<td>4</td>
<td></td>
<td><input type="checkbox" name="math" value="MAC2311"></td>
</tr>
<tr>
<td>MAC2312</td>
<td>Calculus II w/ Analytic Geometry</td>
<td>4</td>
<td>MAC2311 or MAC2281</td>
<td><input type="checkbox" name="math"value="MAC2312"></td>
</tr>
<tr>
<td>MAC2281</td>
<td>Calculus for Engineers I</td>
<td>4</td>
<td></td>
<td><input type="checkbox" name="math" value="MAC2281"></td>
</tr>
<tr>
<td>MAC2282</td>
<td>Calculus for Engineers II</td>
<td>4</td>
<td>MAC2311 or MAC2281</td>
<td><input type="checkbox" name="math" value="MAC2282"></td>
</tr>
<tr>
<td>Math Elective</td>
<td>(Math Elective)</td>
<td>4</td>
<td>MAC2312 or MAC2282</td>
<td><input type="checkbox" name="math" value="math_elective"></td>
</tr>
</tbody>
so, for example, if MAC2311, MAC2312, and math_elective are checked, tbody's background color can change green to signify completion of the section.
You can select all of the checked inputs with the :checked selector:
$("input[type=checkbox]:checked")
To see if any of the checkboxes are checked:
var isAtLeastOneChecked = ($("input[type=checkbox]:checked").length > 0);
if (isAtLeastOneChecked) {
// color your tbody
}
You can use the change handler like
.selected {
background-color: green;
}
then
jQuery(function ($) {
$('input[name="math"]').change(function () {
var $tbody = $(this).closest('tbody');
$tbody.toggleClass('selected', $tbody.find('input[name="math"]:checked').length == 3)
})
})
Demo: Fiddle
Here we adds the class selected to the tbody if there is 3 checked checkboxes
$(document).ready(function(){
$("#youTableId input").click(function () {
//Now $(this) will point to the element that has raised the event
and you can do something like this
alert($(this).is(':checked'));
//Now you now $(this) is the element that raised the event.
//You can get all the attributes of the element like name, id value, whether its check or not etc by using attr() function.
$(this).attr("value");
});
}
You probably want to assign ID's to your input and td elements.
var MAC2311 = document.getElementById('idOfThatInput');
var 2311td = document.getElementById('idOftd');
if (MAC2311.checked)
("#2311td").css("background", "green");
or use an Array containing the elements desired to be checked, and do the comparison in a for loop instead of individually.

How to handle a TR click without the first and last TD

I have a Datatables Table with some random values in it. I would like to create a popup when the client clicks on the TR itself, but NOT on the first and the last TD of the table.
<table class="table href="#popup">
<tr id="tr1">
<td><input type="checkbox"></td>
<td>Test1</td>
<td>Test1</td>
<td><input type="checkbox"></td>
</tr>
<tr id="tr2">
<td><input type="checkbox"></td>
<td>Test1</td>
<td>Test1</td>
<td><input type="checkbox"></td>
</tr>
<tr id="tr3">
<td><input type="checkbox"></td>
<td>Test1</td>
<td>Test1</td>
<td><input type="checkbox"></td>
</tr>
<tr id="tr4">
<td><input type="checkbox"></td>
<td>Test1</td>
<td>Test1</td>
<td><input type="checkbox"></td>
</tr>
</table>
My popup plugin works like, if an href link is called and the popup div's id equals to that href value, it automatically pops up.
However if someone clicks on the first or the last TD do NOT want the popup to activate. Is it actually possible to achieve this somehow?
(The following solution should not be mentioned, because it would make the code look like a mess literally: if I select all the TD fields without the first and last, and add a href attribute to all of the selected TD elements.)
Any other suggestions are welcomed!
When you click, the event is propagated from the child nodes to the parent nodes (learn more here).
You can disable event propagation in both td:first-child and td:last-child elements inside your table in order to prevent your tr event handler from being reached.
I'd also suggest you to use event delegation to keep better performance.
$('.table').on('click', 'tr', function() {
alert('show popup');
});
$('.table').on('click', 'td:first-child, td:last-child', function(e) {
e.stopPropagation();
});
FIDDLE: http://jsfiddle.net/6QTrL/1/
Just use this:
Using :first-child and :last-child with not()
$('table tbody tr td').not(":first-child").not(":last-child").click(function(
//This will only be triggered on the td that are not the first or the last on a tr
))
Here's a fiddle to accomplish that - First and Last row now clickable
I have the first and last row throwing an alert but that's just to give you an idea of how to target them.
$(function(){
var tableRows = $('table').find('tr');
$('table').on('click', 'tr', function(){
if (this == tableRows[0])
alert('first row');
else if (this == tableRows[tableRows.length - 1])
alert('last row');
else
alert('somewhere in the middle');
});
});
The code below is probably more along the lines of what you're looking for. I made the code above in the fiddle so I just pasted that as well.
$(function(){
var tableRows = $('table').find('tr');
$('table').on('click', 'tr', function(){
if (this != tableRows[0] && this == tableRows[tableRows.length - 1])
alert('somewhere in the middle');
});
});

getting the row values with checkbox

hello guys? can you please help with this? i have this tables in HTML.what i want to achieve is that, when i click the row the checkbox will be checked and the row will be highlighted.and is it possible with the checkbox column hidden?
<table border="1" id="estTable">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox"></td>
<td>Chris</td>
<td>10</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Cass</td>
<td>15</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Aldrin</td>
<td>16</td>
</tr>
</tbody>
</table>
<input type="button" value="Edit" id="editbtn"/>
<div id="out"></div>
and i have this javascript to get the values of the selected row.And i was hoping to print one row at a time.
$('#editbtn').click(function(){
$('#estTable tr').filter(':has(:checkbox:checked)').find('td').each(function() {
$('#out').append("<p>"+$(this).text()+"</p>");
});
});
This gets a little easier when you use classes to add more context to your source:
<tr>
<td class="select hidden">
<input type="checkbox">
</td>
<td class="name">Chris</td>
<td class="age">10</td>
</tr>
Then you can do something like this:
$(document).ready(function () {
'use strict';
$('#estTable tbody tr').click(function (e) {
//when the row is clicked...
var self = $(this), //cache this
checkbox = self.find('.select > input[type=checkbox]'), //get the checkbox
isChecked = checkbox.prop('checked'); //and the current state
if (!isChecked) {
//about to be checked so clear all other selections
$('#estTable .select > input[type=checkbox]').prop('checked', false).parents('tr').removeClass('selected');
}
checkbox.prop('checked', !isChecked).parents('tr').addClass('selected'); //toggle current state
});
$('#editbtn').click(function (e) {
var selectedRow = $('#estTable .select :checked'),
tr = selectedRow.parents('tr'), //get the parent row
name = tr.find('.name').text(), //get the name
age = parseInt(tr.find('.age').text(), 10), //get the age and convert to int
p = $('<p />'); //create a p element
$('#out').append(p.clone().text(name + ': ' + age));
});
});
Live demo: http://jsfiddle.net/Lf9rf/
if i understand the "print one row at a time" correctly, i think you need to empty your "out" selector before executing the new call
$('#editbtn').click(function(){
$('#out').empty();
$('#estTable tr').filter(':has(:checkbox:checked)').find('td').each(function() {
$('#out').append("<p>"+$(this).text()+"</p>");
});
});
jsBin demo
CSS:
.highlight{
background:gold;
}
jQuery:
$('#estTable tr:gt(0)').click(function( e ){
var isChecked = $(this).find(':checkbox').is(':checked');
if(e.target.tagName !== 'INPUT'){
$(this).find(':checkbox').prop('checked', !isChecked);
}
$(this).toggleClass('highlight');
});

Categories