Disable click after 5 clicks - javascript

After I click 5 times on table:eq(0) td I want to disable the first function and after i click 2 times on table.dvojka td I want to disable the second function.
$("table:eq(0) td").click(function () {
$(this).addClass("tdbarva");
});
$("table.dvojka td").click(function () {
$(this).addClass("barvica");
});

You'd first need to create at least two global counters that keeps track of the clicks. Then in each click event handler you'd have to check if the clicks match your threshold. From there you use off() to remove the event handlers from each <td/>.
let clickCountOne = 0;
let clickCountTwo = 0;
$("table:eq(0) td").click(function() {
clickCountOne++;
if (clickCountOne === 5) {
console.log('Click handler has been disabled for first table td');
$(this).off('click');
}
$(this).addClass("tdbarva");
});
$("table.dvojka td").click(function() {
clickCountTwo++;
if (clickCountTwo === 2) {
console.log('Click handler has been disabled for second table td');
$(this).off('click');
}
$(this).addClass("barvica");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<td>Click Me</td>
</table>
<table class="dvojka">
<td>Click Me Too</td>
</table>

I might suggest using a data attribute on the table itself to keep count of clicks. When either table is clicked, its count attribute is updated. Then its event listener is called, which compares that dynamic count against a static limiter (manually typed into the DOM node). If the count exceeds the limit, i use the jQuery off() function to remove that table's event handler.
Hope this helps!
// Set my click counter to zero for all tables...
$("table").each(function() {
$(this).data("clickCount", 0);
});
// Create the references to each table element.
var firstTable = $("table:eq(0)");
var secondTable = $("table.dvojka");
// Attach my event listeners...
firstTable.on("click", "td", firstFunc);
secondTable.on("click", "td", secondFunc);
/****
* Each table will maintain its own click count data attribute.
*
****/
$("table td").on("click", function() {
var clickedTable = $(this).parents("table");
var clickCount = parseInt(clickedTable.data("clickCount")) + 1;
var clickLimit = clickedTable.attr("data-clickLimiter");
clickedTable.data("clickCount", clickCount);
});
/*****
* The following functions are used in the event listeners for the
* tables, and are tracking their own count to determine when to
* disable themselves.
*****/
function firstFunc(evt){
// the clickCount is dynamic, created by the program itself.
// The clickLimiter is a static attribute, defined on the DOM node manually.
var clickCount = parseInt(firstTable.data("clickCount"));
var clickLimit = parseInt(firstTable.attr("data-clickLimiter") );
// Has the count exceeded our limit?
if(clickCount >= clickLimit){
// If it has, remove the event listener.
firstTable.off("click", "td", firstFunc);
}
console.log("You've clicked the first table "+
clickCount +
" times. It has a limit of " +
clickLimit +
" clicks, or " +
parseInt(clickLimit-clickCount) +
" remaining");
}
function secondFunc(){
var clickCount = parseInt(secondTable.data("clickCount"));
var clickLimit = parseInt(secondTable.attr("data-clickLimiter") );
if(clickCount >= clickLimit){
secondTable.off("click", "td", secondFunc);
}
console.log("You've clicked the second table "+
clickCount +
" times. It has a limit of " +
clickLimit +
" clicks, or " +
parseInt(clickLimit-clickCount) +
" remaining");
}
.dvojka {
background-color: #ccc;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table data-clickLimiter=5>
<thead>
<tr>
<th>Foo</th>
<th>bar</th>
<th>baz</th>
</tr>
</thead>
<tbody>
<tr>
<td>01-01</td>
<td>01-02</td>
<td>01-03</td>
</tr>
<tr>
<td>02-01</td>
<td>02-02</td>
<td>02-03</td>
</tr>
<tr>
<td>03-01</td>
<td>03-02</td>
<td>03-03</td>
</tr>
</tbody>
</table>
<table class="dvojka" data-clickLimiter=2>
<thead>
<tr>
<th>Foo</th>
<th>bar</th>
<th>baz</th>
</tr>
</thead>
<tbody>
<tr>
<td>01-01</td>
<td>01-02</td>
<td>01-03</td>
</tr>
<tr>
<td>02-01</td>
<td>02-02</td>
<td>02-03</td>
</tr>
<tr>
<td>03-01</td>
<td>03-02</td>
<td>03-03</td>
</tr>
</tbody>
</table>
Note that this could just as easily have been done with a single click handler for both tables, and it would only disable for the appropriate table -- but I can only assume that the processing for both tables would somehow be different. If both tables have the exact same click functionality, then it would be a trivial matter to remove the second table's click handler, and simply have the one function for all tables.

Related

Opacity 0 Javascript function

This is my first SO post, please let me know how to do better!
I have a function that clears the by setting the opacity to 0, it works, but it will make my file massive if if try to set up a whole spread sheet with each having the same function bar different ids,
Ideally, the way I want this to play out, is that clears itself, and will clear all blocks. And I want to do it without having to write duplicate functions.
Is it possible to have a function set over classes? I have tried with no success
Or is there a better way to run the JavaScript, like somehow onclick==clear.self ?
function Xf1() {
f1();
f2();
}
function f1() {
var element = document.getElementById("a1");
element.style.opacity = "0";
}
function f2() {
var element = document.getElementById("a2");
element.style.opacity = "0";
<tr>
<th onclick="Xf1()">Clear all</th>
</tr>
<tr>
<td onclick="f1()" id="a1"> text1</td>
<td onclick="f2()" id="a2"> text2</td>
</tr>
You can use event delegation
start by adding a class to the table element
add a class to the "clear all" heading
add a click event listener to the table element
If the click event target is a td element, set its opacity to 0.
If the click target is the clear all heading, set all td elements to opacity 0. You can do that by querying the table for td tags and then using forEach to change the opacity for each of them.
const myTable = document.body.querySelector(".my-table");
myTable.addEventListener("click", event => {
const target = event.target;
if (target.tagName == "TD") {
target.style.opacity = 0;
}
if (target.classList.contains("clear-all")) {
myTable.querySelectorAll("td").forEach(item => (item.style.opacity = 0));
}
});
<table class="my-table">
<thead>
<th class="clear-all">Clear all</th>
</thead>
<tbody>
<tr>
<td id="a1">text1</td>
<td id="a2">text2</td>
</tr>
</tbody>
</table>
set an id to parent tag then set onclick to that elements children.
if you set "container" as id you will have something like this:
var elements = document.getElementById("container");
for (let i = 0; i < elements.children.length; i++) {
elements.children[i].onclick = function () {
elements.children[i].style.opacity = "0";
};
}
Instead of adding onclick to each td element. Have it in table element. Like this
document.querySelector("#table").addEventListener("click", (event)=>{
if(event.target.dataset.type==='clear'){
const ids = ['a1', 'a2'];
ids.forEach((ele)=>{
document.querySelector(`#${ele}`).style.opacity = '0';
});
return;
}
event.target.style.opacity = "0";
}
)
<table id="table">
<tr>
<th data-type="clear">Clear all</th>
</tr>
<tr id="tableRows">
<td id="a1"> text1</td>
<td id="a2"> text2</td>
</tr>
</table>

Assigning an event to a <td> cell via a function

I'm trying to understand why I can add certain items to a cell, such as 'id', and not other items such as an onclick. My goal is to have a button pressed, which adds a row to a table (which works) - and set some values on the that is generated/appended to the table. I've noticed that I can step into the console and do:
rows[row_#].cells[cell_#].id = 'foo';
and have it appear in the table on the and function; but the following will not appear on the :
rows[row_#].cells[cell_#].onclick = 'callEvent(this)';
Should I be assigning this differently?
<button type="button" id="btn_add_row" onclick="addRow()">Add Row</button>
<table class="table table-hover" id="sample_table">
<thead>
<th>Column A</th>
<th id='calculate'>Column B</th>
</thead>
<tbody>
<tr>
<td>Item 1</td>
//sample of the td I'd like the function to generate
<td id='calculate' onclick='callEvent(this)'>Item 2</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
// Code to add a row to the table and assign properties to new row
function addRow() {
var table = document.getElementById("sample_table");
var lastRow = table.length;
var numberOfCols = table.rows[0].cells.length;
var row = table.insertRow(lastRow);
for (var i=0;i<numberOfCols;i++) {
row.insertCell(i);
if (table.rows[0].cells[i].id === 'calculate') {
// The calculate id will appear on the TD after running
table.rows[i].id = 'calculate';
// The onclick event will not appear on the TD afer running
table.rows[i].onclick='callEvent(this)';
}
function callEvent(element) {
console.log('Calculate event fired!');
}
</script>
The biggest issue is that you are not supplying a callback function reference to your onclick property. You are supplying a string:
.onclick='callEvent(this)'
So, no function actually gets invoked when the click event occurs.
Next, you shouldn't be using event properties (like onclick) in your JavaScript or adding inline HTML event handling attributes at all (that technique is about 20 years old) as they:
Create "spaghetti code" that is difficult to read and debug.
Lead to duplication of code.
Don't scale well
Don't follow the separation of concerns development methodology.
Create anonymous global wrapper functions around your attribute values that alter the this binding in your callback functions.
Don't follow the W3C Event Standard.
Instead, do all your work in JavaScript and use .addEventListener() to set up event handlers.
Also (FYI) id attributes need to be unique, so when you create a new row or cell, don't reuse an already assigned id.
Here's an example:
// Place all of this inside of a <script> element that is just before the
// closing of the body (</body>)
// Get references to all elements that you'll be working with
var btnAddRow = document.getElementById("btn_add_row");
var tbl = document.getElementById("sample_table");
// Now, set up the event handling functions
btnAddRow.addEventListener("click", addRow);
// Code to add a row to the table and assign properties to new row
function addRow() {
var counter = 1; // id attributes must be unique. This will keep it that way.
var numberOfCols = tbl.rows[0].cells.length;
var row = tbl.insertRow();
for (var i = 0; i < numberOfCols; i++) {
var cell = row.insertCell(i);
cell.id = "row" + (tbl.rows.length - 1) + "cell" + counter;
// Now, we'll create a new button, place that button in the new cell and
// set up a click event handler for it.
var btn = document.createElement("button");
btn.textContent = cell.id;
btn.id = "btn" + tbl.rows.length + counter;
// Add a click event handler
btn.addEventListener("click", function(){
alert("You clicked cell: " + this.id);
});
// And now include the button in the cell
cell.appendChild(btn);
counter++; // Increment the counter after using it
}
}
td { border:1px solid black; }
td:nth-child(2) { cursor:pointer; }
<button type="button" id="btn_add_row">Add Row</button>
<table class="table table-hover" id="sample_table">
<thead>
<th>Column A</th>
<th id='calculate'>Column B</th>
</thead>
<tbody>
<tr>
<td>Item 1</td>
<!-- sample of the td I'd like the function to generate -->
<td id='calculate'>Item 2</td>
</tr>
</tbody>
</table>
Two things:
The onclick expects a function. So to solve your problem, change
table.rows[i].onclick='callEvent(this)';
to
table.rows[i].onclick=callEvent;
The second thing is, the parameter on an event is actually the event, and this refers to the element:
function callEvent(event) {
console.log('Calculate event fired!');
// "event" is the event
// "this" is the element
}
missing need to second bracket end and use this callEvent(this) without single inverted comma.
Like this...
<script type="text/javascript">
// Code to add a row to the table and assign properties to new row
function addRow() {
var table = document.getElementById("sample_table");
var lastRow = table.length;
var numberOfCols = table.rows[0].cells.length;
var row = table.insertRow(lastRow);
for (var i=0;i<numberOfCols;i++) {
row.insertCell(i);
if (table.rows[0].cells[i].id === 'calculate') {
// The calculate id will appear on the TD after running
table.rows[i].id = 'calculate';
// The onclick event will not appear on the TD afer running
table.rows[i].onclick=callEvent(this);
}
}
}
function callEvent(element) {
console.log('Calculate event fired!');
}
</script>

Changing td to textbox when clicked and changing textbox back to td when user is done

I'm looking for a way the user can change a td from table by clicking on it. My current setup is when a user clicks on a td, it should replace the clicked td by a textbox which contains the text from the clicked td. Then when the user clicks outside of it, or presses enter. It should change the textbox back to td.
This is the Table I have:
<table class="table" id="tableCompleted">
<thead>
<tr>
<th>Name</th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>Yes</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Jesse</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>David</td>
<td>No</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
</tbody>
</table>
This is the script I have written, the problem I have is that, when a users clicks on a td it replaces all the td's and I havent figured out how to change it back when it loses focus. I tried using a button but that made it even worse. Also the text from the td doesn't get placed inside the textbox.
The script I used for it:
$('td').click(function () {
$('td').replaceWith(function () {
return '<input type="text" id="modify" "value="' + $('#Text').val() + '" />';
});
})
I Currently have 3 problems with it, Im not sure how to only change the td that is clicked, because all the td don't have a unique id or class. And I'm not sure how to change it back when the user is done changing it. Also It doesn't place the content of the td inside the textbox.
For referring to the clicked element you can use the this keyword:
$('td').on('click', function() {
$(this)...
});
Also you shouldn't replace the td with an input, because a trelement can't have an input child. Instead of it remove the textContent of the td and append an input to it. Here is one way of implementing this:
$('td').on('click', function() {
var $this = $(this);
var $input = $('<input>', {
value: $this.text(),
type: 'text',
blur: function() {
$this.text(this.value);
},
keyup: function(e) {
if (e.which === 13) $input.blur();
}
}).appendTo( $this.empty() ).focus();
});
Here is a demo using the above snippet.
You have also a more sensible option: contentEditable property.
$('td').prop('contentEditable', true);
Using the above property browser handles the editing. You can either manually add a contentEditable to the target cells in your markup or use JavaScript for modifying the attribute.
Pure javascript solution here. And additionaly jsfiddle demo
Fight the crime :)
(function(){
'use-strict';
var td = document.getElementsByTagName('td');
for(var i=0;i<td.length;i++) {
td[i].addEventListener('click', change, false);
}
function change() {
if(this.firstChild.nodeType !== 3) {
return;
}
var docFrag = document.createDocumentFragment();
var input = document.createElement('input');
input.value = this.textContent;
this.removeChild(this.firstChild);
docFrag.appendChild(input);
this.appendChild(docFrag);
}
}());
You can use this. Here is a working demo.
$('td').click(function () {
$(this).replaceWith(function () {
return '<input type="text" id="modify" value="' + $(this).text() + '"> </input>';
});
})
To get it back you can use attach focusout on this.

Toggle table rows with jquery

I have a normal html table.
Each <tr> has attributes data-id and data-parent-id.
I want to toggle underlying <tr> when I click on a <tr>.
I have made a short bad solution:
$("tr[data-id]").click(function () {
var id = $(this).attr("data-id"); // get id from clicked tr
$(this).siblings('tr[data-parent-id="' + id + '"]').each(function () { // iterate through all tr under clicked tr
$(this).toggle(); // toggle first level under clicked tr
var id = $(this).attr("data-id"); // get id from first level under clicked tr
$(this).siblings('tr[data-parent-id="' + id + '"]').each(function () { // iterate through all tr under first level under clicked tr
$(this).toggle(); // toggle _second level_ under clicked tr
var id = $(this).attr("data-id"); // get id from _second_ level under clicked tr
$(this).siblings('tr[data-parent-id="' + id + '"]').each(function () { // iterate through all tr under second level under clicked tr
$(this).toggle(); // toggle _third_ level under clicked tr
});
});
});
});
In my example I loop through each level but I don't know how to make a "never ending" traversing. I have specified only 3 levels of <tr> but it should be limitless.
It seems that http://ludo.cubicphuse.nl/jquery-treetable/ does what I want but I think it could be done much simpler and when I have a huge table I think it's better to hide the collapsed table rows with css instead of waiting for the javascript to do this (it flickers because of the loading).
How can I make a loop that checks for every <tr> if any other <tr> has its data-id as data-parent-id?
Edit:
Html
<table>
<tbody>
<tr data-id="1" data-parent-id=""></tr>
<tr data-id="2" data-parent-id="1"></tr>
<tr data-id="3" data-parent-id="1"></tr>
<tr data-id="4" data-parent-id="3"></tr>
<tr data-id="5" data-parent-id="4"></tr>
</tbody>
</table>
You need recursivity :
$("tr[data-id]").click(function () {
Expand($(this).attr("data-id"));
}
function Expand(id)
{
$('tr[data-parent-id="' + id + '"]').each(function () {
$(this).toggle();
Expand($(this).attr("data-id"));
});
}
You got to be carefull with recursivity cause it may create and endless loop. In this case it will stop when it will reach a tr that as no data-parent-id (the root).

Filter td elements in a tr out, that are not displayed

I have a Datatable in asp.net that i want to modifiy. I select the <tr> rows of the Datatable with JQuery:
var rows = $("#dgInformation tr:gt(0)");
However, the <tr>elements have multiple <td>elements and some of them are marked as display:none. How can i get the rows-variable without those hidden cells?
The purpose of this is to check cells if they are different from each other and only one line for each difference should be displayed. If i dont filter the not displayed elements, they get also compared and i have lines, which are visually the same.
Update
It works with just adding a CSS class to the <td>-elements that should be hidden. Then you have a clean DOM-Tree (i hope i can call it this way) in Firebug. The whole function below for reference:
function filterTable()
{
var rows = $("#dgInformation tr:gt(0)");
var prevRow = null;
var counter = 2;
rows.each(function (index) {
if (prevRow !== null) {
var i = 1;
var changes = 0;
$(this).children("td:visible").each(function () {
if(i > 2){
if ($(':nth-child(' + i + ')', $(prevRow)).html() != $(this).html())
{
$(':nth-child(' + i + ')', $(prevRow)).css('backgroundColor', '#00FF00');
changes = changes + 1;
}
}
i++;
});
if(changes == 0)
{
$(prevRow).css('display','none');
$(prevRow).removeClass();
}
else
{
$(prevRow).removeClass();
if(counter % 2 == 0)
$(prevRow).addClass('dgItemStyle');
else
$(prevRow).addClass('dgAlternatingItemStyle');
counter = counter + 1;
}
}
prevRow = this;
});
}
You can use the :not() filter for this.
If you have html like
<table id='tableid'>
<tr><td></td>
<td class="hidden">Hidden Cell</td>
<td>3</td>
<td>4</td></tr>
<tr>
<td>5</td>
<td class="hidden">Hidden Cell</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>8</td>
<td class="hidden">Hidden Cell</td>
<td>9</td>
<td>1</td>
</tr>
</table>
CSS:
.hidden{
display:none;
}
Script:
If u want all the td elements in that table without hidden td elements then,
var rows = $("#dgInformation tr:gt(0) td:not('.hidden')");
It would work!!
Try this:
var rows = $("#dgInformation tr:gt(0)").find('td').not(':visible').remove();
I see you're using jQuery.. So would
var rows = $("#dgInformation tr:gt(0)").not($("#dgInformation tr:gt(0)").has("td[display=none]"));
work?
Since you only want the td's that are visible, the following should suffice:
var visible_cells = $("#dgInformation tr:gt(0)").find("td:visible");
If you want the ones that are not visible, use :hidden instead :)
Update
I'm not sure what you mean by "How can i get the rows-variable without those hidden cells?".
If I read that correctly, you basically want the row, and have it's non-visible cells removed from the variable but kept in HTML.
This can't be done, as the variable you hold is still based on the actual HTML. The row-variable will not even contain any of the children. They will be looked up in the DOM when you request them specifically.
However, I assume at some later stage in your code, you actually get the selected row's <td> children. Apply the :visible filter on them whenever you need them, or make a second variable which holds the children you want, like I suggested in my answer.
You just can't pre-emptively delete them from a variable without altering the HTML.

Categories