Prevent parent click being fired when dragging outside child editable element - javascript

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>

Related

React table cell getSelection()

I have a table with an onRowClick handler on the <tr/> element, I want to prevent a click event if I select text inside it, my solution was this:
const getSelectionWithoutClick = (originalHandler: () => void) => {
if (!getSelection()?.toString()) originalHandler();
};
What I found odd is, when I select a text inside a cell, and click on the same cell, the click event is not fired until the selection is cleared, but if click on another cell (even in the same row), while text is selected in the original cell, the click event fires. You know why this happens??
I think you can differentiate between click and select event based on onMouse up
function TablePage() {
function onSelect(e) {
let selection = window.getSelection().toString();
if (selection === '') {
console.log('click');
} else {
console.log('selection', selection);
}
}
return <table>
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Country</th>
</tr>
</thead>
<tbody>
<tr>
<td onMouseUp={onSelect}>Alfreds Futterkiste</td>
<td onMouseUp={onSelect}>Maria Anders</td>
<td onMouseUp={onSelect}>Germany</td>
</tr>
</tbody>
</table>
}
export default TablePage;

Trouble with event bubbling and JavaScript checkbox

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>

How to prevent $event fo fire twice

I'm creating a table component in AngularJS but i'm having a bug that i can't solve.
The structure of my table is something like:
<table>
<thead>...</thead>
<tbody>
<tr ng-click="selectRow()" ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value)>
<td><input type="checkbox" ng-model="$value.__checked" ng-click="selectRow($index,$value)"/></td>
</tr>
</tbody>
</table>
When i click on the row, it works fine, but when i click on the checkbox it doesn't work as expected, and fire the event twice. I thought that just using the stopPropagation() on selectRow when it's fired by the checkbox would work, but it doesn't.
The function selectRow can work for multi and single seleciton.
function selectRow(ngRepeatIndex,ngRepeatValue){
var selectedValues = $scope.$parent.selectedValues;
cleanValueAndArrays(vm.checkAll,vm.checkAll);
if($attrs.onClick)vm.onClick({value: ngRepeatValue});
if(vm.config.selection == 'single'){
if(ngRepeatValue.__checked){
ngRepeatValue.__checked = false;
cleanArrays();
} else {
cleanValueAndArrays(vm.selectedIndexes.length > 0)
pushToArrays(ngRepeatValue,ngRepeatIndex);
ngRepeatValue.__checked = true;
}
} else {
ngRepeatValue.__checked = vm.selectedIndexes.filter(function(val){return val == ngRepeatIndex}).length < 1;
if((ngRepeatValue.__checked) || vm.selectedIndexes.length == 0 ){
pushToArrays(ngRepeatValue,ngRepeatIndex);
return 0;
}
var indexOfValueSelected;
selectedValues.forEach(function(val,indx){
if(angular.equals(val,ngRepeatValue)) indexOfValueSelected = indx;
})
$scope.$parent.selectedValues.splice(indexOfValueSelected, 1);
vm.selectedIndexes.splice(vm.selectedIndexes.indexOf(ngRepeatIndex),1);
}
}
Remove the second ng-click, it is useless, the ng-click on the row will be called if you click on your checkbox.
Create a directive to stop event propagation.
moduleName.directive('preventDefault', function () {
return function (scope, element, attrs) {
$(element).click(function (event) {
event.preventDefault();
});
}
});
You can use it as below:
<table>
<thead>...</thead>
<tbody>
<tr ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value) prevent-default>
<td><input type="checkbox" ng-model="$value.__checked"/></td>
</tr>
</tbody>
</table>
try this :
<table>
<thead>...</thead>
<tbody>
<tr ng-click="selectRow()" ng-repeat="$value in values track by $index" ng-click="selectRow($index,$value)>
<td><input type="checkbox" ng-model="$value.__checked" ng-click="selectRow($index,$value); $event.stopPropagation();"/></td>
</tr>
</tbody>
</table>

In each table check background color and validate

I have a table with some row colored as green.Each row have a checkbox.
When I click submit button i need to validate that only green colored row whose checkboxes are not checked should be checked.
No other colored rows and just the green one(#47A347).
Below is my html.Can anyone help me getting the solution.
<form method="post" action="test2.html">
<table>
<tr bgcolor="#47A347" class="rowb">
<td>Hello</td>
<td><input type="checkbox" id="chk" class="linebox"></td>
</tr>
<tr bgcolor="#47A347" class="rowb">
<td>Hello 1</td>
<td><input type="checkbox" id="chk1" class="linebox"></td>
</tr>
<tr class="rowb">
<td>Hello 2</td>
<td><input type="checkbox" id="chk1" class=""></td>
</tr>
<tr>
<td><input type="submit" id="btn" value="Submit"></td>
</tr>
</table>
</form>
I have tried below jquery code.Though it works it fails sometimes.
<script>
jQuery(document).on('click', '#btn', function (event)
{
var rv = true;
$(".rowb").each(function()
{
if($(this).css("background-color") == "rgb(71, 163, 71)")
{
var ischk = 0;
var row = $(this);
if (row.find('input[class="linebox"]').is(':checked') )
{
ischk++;
}
if(ischk==0)
{
rv=false;
}
}
});
if (!rv)
{
alert('Please check');
event.preventDefault();
}
});
</script>
Try this snippet. Should give you an alert for each green checkbox that has not been checked on click of the submit 'btn'. If there is a green row checkbox that has not been checked, the default submit action will be stopped.
$(document).ready(function(){
$('#btn').on('click', function(){
var i = 1;
var error = false;
$(".rowb").each(function() {
ischk = 0;
if($(this).attr("bgcolor") == "#47A347") {
if (!$(this).find('input.linebox').is(':checked') )
{
alert('Please check green checkbox #' + i);
error = true;
}
i++;
}
});
if (error){
event.preventDefault();
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form method="post" action="test2.html">
<table>
<tr bgcolor="#47A347" class="rowb">
<td>Hello</td>
<td><input type="checkbox" id="chk" class="linebox"></td>
</tr>
<tr bgcolor="#47A347" class="rowb">
<td>Hello 1</td>
<td><input type="checkbox" id="chk1" class="linebox"></td>
</tr>
<tr class="rowb">
<td>Hello 2</td>
<td><input type="checkbox" id="chk1" class=""></td>
</tr>
<tr>
<td><input type="submit" id="btn" value="Submit"></td>
</tr>
</table>
</form>
Instead of asserting in background-color try checking for bgcolor attribute.
//if($(this).css("background-color") == "rgb(71, 163, 71)")
if( $(this).attr("bgcolor") == "#47A347" )
Here's the full refactored code:
jQuery(document).on('click', '#btn', function (event)
{
var rv = true;
$(".rowb").each(function()
{
if($(this).attr("bgcolor") == "#47A347")
{
if ( !$(this).find('.linebox').is(':checked') )
{
rv = false;
return false
}
}
});
if (!rv)
{
alert('Please check');
event.preventDefault();
}
});
$('#btn').on('click', function(){
var data = {};
var form = $(this).closest('form');
$('[bgcolor="#47A347"]', form).each(function(){
data[this.id] = $(this).find('input').val();
})
});
Note: you didn't provide name attribute for inputs. With name attribute provided you can use jQuery's serialize method to gather form data automatically. To filter out unneeded fields you can temporarily set them to disabled state.

Checkbox click overriding table row click

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>

Categories