Javascript - free a memorized function - javascript

I must say that I do not know the right terminology, so please forgive my some mistakes.
What I have is a Table with rows. The last editable column of the last row in the table is enabled with a TabKey which automatically creates a new row.
function enableTab() {
lastEditable = currentTable
.find("tr")
.find('textarea, .cleanbox-editable-cell')
.not(function(index, element) {
return $(element).parent().css("display") == "none";
})
.last();
lastEditable.unbind("keydown");
lastEditable.keydown(function(e) {
var keyCode = e.keyCode || e.which;
createNewRow();
};
};
function createNewRow() {
createRow();
enableTab();
}
When the new row is created, the lastEditable variable is re-calculated.
What I want is lastEditable to be associated only to the last column of the last row. Anyway, what happens is that a new row is created, and both the new and previous rows' last columns are furnished with the function to create new rows, when I want only the new row to be be able to perform such a function.
So, in my opinion, what happens is that: everytime a new row is created, the variable lastEditable is rightly associated to the last column of the new row. But the function of the object previously associated to that same variable is not deleted: it simply doesn't have a name anymore.
Am I kind of right? How can I delete an object?
I tried the following, but it didn't work:
lastEditable.keydown(function(e) {
var keyCode = e.keyCode || e.which;
delete lastEditable;
createNewRow();
};

You are unbinding the event handler, then immediately reapplying the same event handler to the same element. You need to re-organise your code so that you bind the event handler to the new element as you create the new row http://jsfiddle.net/2fLrsjcz/
var currentTable = $('table');
function bindKeyDown(lastEditable) {
lastEditable.keydown(function(e) {
if(e.keyCode == 9) {
createNewRow($(this));
}
});
}
function getLastEditable() {
return currentTable
.find("tr")
.find('textarea, .cleanbox-editable-cell')
.not(function(index, element) {
return $(element).parent().css("display") == "none";
})
.last();
}
function createNewRow($lastEditable) {
var newEditable = $lastEditable.parent().parent().clone().appendTo(currentTable);
$lastEditable.unbind("keydown");
bindKeyDown(getLastEditable());
}
bindKeyDown(getLastEditable());

To improve this post that may be useful to others, studying Lee's code I have solved my problem just moving lastEditable.unbind("keydown") into .keydown(function(e) {}), as following:
function enableTab() {
lastEditable = currentTable
.find("tr")
.find('textarea, .cleanbox-editable-cell')
.not(function(index, element) {
return $(element).parent().css("display") == "none";
})
.last();
lastEditable.keydown(function(e) {
var keyCode = e.keyCode || e.which;
lastEditable.unbind("keydown");
createNewRow();
};
};
function createNewRow() {
createRow();
enableTab();
}

Related

Jquery plugin with prototype

I was trying on the basics of Jquery plugin and the prototype concept, but ended up in an unusual behavior.
HTML :
<div>
<span>
<textarea>Text Area with 500 characters. Adding Some text.</textarea>
<span class="cl"></span>
</span>
<span>
<textarea>Text Area with 100 characters</textarea>
<span class="cl"></span>
</span>
</div>
JQuery :
(function ($) {
var tisCharsLeftCntxt = null;
function fnCharsLeft(ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
tisCharsLeftCntxt = this;
this.fnInit();
}
fnCharsLeft.prototype = {
fnInit: function () {
tisCharsLeftCntxt.fnUpdateRemainingChars();
tisCharsLeftCntxt.jqe.keyup(function (event) {
key = event.keyCode ? event.keyCode : event.which;
if ((37 != key) && (38 != key) && (39 != key) && (40 != key)) {
tisCharsLeftCntxt.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = tisCharsLeftCntxt.maxChars - tisCharsLeftCntxt.jqe.val().length,
jqeDestToUpdt = tisCharsLeftCntxt.jqe.siblings('.cl');
charsLft = (charsLft < 0) ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + tisCharsLeftCntxt.maxChars + ' characters');
} else {
tisCharsLeftCntxt.jqe.val(tisCharsLeftCntxt.jqe.val()
.substring(0, tisCharsLeftCntxt.maxChars));
tisCharsLeftCntxt.jqe.scrollTop(tisCharsLeftCntxt.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + tisCharsLeftCntxt.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
})(window.jQuery);
$('div span:nth-child(1) textarea').fnCharsLeftPlgn({maxChars: 500});
$('div span:nth-child(2) textarea').fnCharsLeftPlgn({maxChars: 100});
Fiddle :
http://jsfiddle.net/5UQ4D/ & http://jsfiddle.net/5UQ4D/1/
Requirement is, the plugin should show the number of characters that can be added in a text-area. If there is only one text-area in a page this is working good. But if there are more than one, only the text-area which is last associated with the plugin is working properly.
With respect to code here, In both the text-area number of characters left is updated correctly during initialization (only for the first time). But later when the text area content is changed, only the second with 100 chars (or the most recent text-area associated with the plugin) is working properly.
Seems like, I'm failing to restrict the plugin context independently to a text-area. Please Advice,..
Problem 1:
As mentioned in the comments, you're creating a variable named tisCharsLeftCntxt outside of the other contexts, then assigning this to it in your constructor. Every time you run your plugin you stomp on tisCharsLeftCntxt with a new this.
There is no reason to use a reference to this in the wholesale fashion in which you have. There is only one place in your code where the scope changes such that this is no longer your instance. That place is inside of the keyup event handling function. You should localize your aliasing of this to just the method which contains that event handler.
Problem 2:
I believe another part of your problem (this would be seen if you ran the plugin against a selector which matched more than one element) is inside of the plugin function (the one which lives off of $.fn).
$.fn.fnCharsLeftPlgn = function (genStngs) {
return $(this).data("charsleft", new fnCharsLeft(this, genStngs));
};
It should be:
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data("charsleft", new fnCharsLeft(this, genStngs));
});
};
When directly inside of a method which has been added to the jQuery prototype ($.fn), this refers to the entirety of the current collection, not an element. A plugin should each itself in order to run element specific logic against its individual members.
Without using .each() you are calling .data() against an entire collection, setting all of their charsleft data properties to the one instance of fnCharsLeft. By using .each() you create a new instance of fnCharsLeft for each of the elements in the collection.
Since the .each() then returns the collection, and a plugin should be chainable, you simply return it.
A rule of thumb is that if you're passing this into the jQuery factory ($()) directly inside of a plugin, function then you're doing something wrong since it is already the collection. As a second rule of thumb, almost all plugin definitions except those which are intended to return info about an element (such as .val(), .html(), or .text() when not given a param) should start with return this.each(function() {...
Solutions:
Bringing those changes together results in this fiddle: http://jsfiddle.net/5UQ4D/4/
And this code:
(function ($) {
var fnCharsLeft = function (ele, genStngs) {
this.jqe = $(ele);
this.maxChars = genStngs.maxChars;
this.fnInit();
};
fnCharsLeft.prototype = {
fnInit: function () {
var instance = this;
this.fnUpdateRemainingChars();
this.jqe.on('keyup', function (e) {
key = e.keyCode ? e.keyCode : e.which;
if (37 != key && 38 != key && 39 != key && 40 != key) {
instance.fnUpdateRemainingChars();
}
});
},
fnUpdateRemainingChars: function () {
var charsLft = this.maxChars - this.jqe.val().length,
jqeDestToUpdt = this.jqe.siblings('.cl');
charsLft = charsLft < 0 ? 0 : charsLft;
if (charsLft) {
jqeDestToUpdt.text(charsLft + ' more of ' + this.maxChars + ' characters');
} else {
this.jqe
.val(this.jqe.val().substring(0, this.maxChars))
.scrollTop(this.jqe[0].scrollHeight);
jqeDestToUpdt.text("Maximum limit of " + this.maxChars + " characters reached");
return false;
}
}
};
$.fn.fnCharsLeftPlgn = function (genStngs) {
return this.each(function () {
$(this).data('charsleft', new fnCharsLeft(this, genStngs));
});
};
}(window.jQuery));

Can't make TAB change the editor on dgrid

I'm trying to make the TAB key navigate on my dGrid. I have used as a base the solution found at Dgrid set focus on cell, but there are a couple of issues I'm running into which I couldn't solve so far.
Below you can find the block I'm using now; Not all columns have editors, so for I added a var do the element definition to select the next column instead of doing a right. I also added support for SHIFT+TAB to make backwards navigation possible. MT4.prje.grids[gridId]is the dGrid instance. There might be various on the page.
The grid is created with
MT4.prje.grids[gridId] = new (declare([OnDemandGrid, Keyboard, Selection, CellSelection]))(gridInfo, gridId);
where gridInfo has the column definitions and the store. The store is created as:
new Observable(new Memory({'data': {}, 'idProperty': 'id'}));
The editors are usually TextBox, NumberTextBox and Select dijit widgets, all set to autoSave.
aspect.after(MT4.prje.grids[gridId], "edit", function (promise, cellNode) {
if (promise === null) return;
promise.then(function (widget) {
if (!widget._editorKeypressHandle) {
widget._editorKeypressHandle = on(widget, "keypress", function (e) {
for (var rowId in MT4.prje.grids[gridId].selection) {
break;
}
for (var columnId in MT4.prje.grids[gridId].selection[rowId]) {
break;
}
if (e.charOrCode == keys.TAB) {
e.preventDefault();
var cellToEdit = null,
cellEdited = MT4.prje.grids[gridId].cell(rowId, columnId);
if (e.shiftKey) {
if (cellEdited.column.previousEditor === undefined) {
rowId = parseInt(rowId) - 1;
if (MT4.prje.grids[gridId].row(rowId).element !== null) {
for (var lastColumnId in MT4.prje.grids[gridId].columns) {}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, lastColumnId);
}
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.previousEditor);
}
} else {
if (cellEdited.column.nextEditor === undefined) {
var firstColumnId = null;
rowId = parseInt(rowId) + 1;
if (MT4.prje.grids[gridId].row(rowId).element === null) {
var fields = {};
for (var cId in MT4.prje.grids[gridId].columns) {
if ((cId != 'excluir') && (firstColumnId === null)) {
firstColumnId = cId;
}
fields[cId] = '';
}
MT4.prje.addRowToGrid(gridId, fields);
} else {
for (var cId in MT4.prje.grids[gridId].columns) {
if (cId != 'excluir') {
firstColumnId = cId;
break;
}
}
}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, firstColumnId);
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.nextEditor);
}
}
if (cellToEdit) {
MT4.prje.grids[gridId].deselect(cellEdited);
MT4.prje.grids[gridId].select(cellToEdit);
MT4.prje.grids[gridId].edit(cellToEdit);
}
}
});
}
});
});
Even ignoring the new line part, there are a couple of errors that happen. First of all, the editor barely pops into existence and them disappears, together with the selection. Sometimes when tabbing to an empty column, the editor will be filled with the values of the previous editor. Is there a way to do it more consistently?
What I'm figuring is that there is a race condition happening on the sharedEditor (they are set to editOn: focus). I tried wrapping the deselect/select on a dojo.on('blur') and emit it. But that doesn't get consistently correct with the dijit/form/Select widgets. Is there a better event that I can call for it?
I also tried changing the final block to:
if (cellToEdit) {
on(cellToEdit.element, 'focus', function(){
MT4.prje.grids[gridId].select(cellToEdit);
});
on(cellEdited.element, 'blur', function(){
MT4.prje.grids[gridId].deselect(cellEdited);
on.emit(cellToEdit.element, 'focus', {'bubble': true, 'cancelable': false});
});
on.emit(cellEdited.element, 'blur', {'bubble': true, 'cancelable': false});
}
But that gives two errors:
If I do make changes to a cell it does not go to the next editor. Does not even select it.
The first time I move from an empty cell to another empty cell it doesn't work either.
Anyone got any ideas?
This fix works on dgrid 0.3.11.
Add to your dgrid's postCreate.
postCreate: function() {
var that = this;
this.inherited(arguments);
this.on('dgrid-datachange', function(evt) {
that._selectedCell = that.cell(evt);
});
aspect.after(this, 'save', function(dfd) {
dfd.then(function() {
var nextCell = that.right(that.cell(that._selectedCell.row.id, that._selectedCell.column.id));
that.edit(nextCell);
// Bonus Fix. Workaround dgrid bug that blocks field text to be selected on focus.
nextCell.element.widget && nextCell.element.widget.textbox && nextCell.element.widget.textbox.select();
});
});
}

Hide/unhide table row given the row index and table ID

Currently I have this code to delete a table row:
var remove = document.getElementById(dataID); (this is the id of an object in the row I wish to hide)
if(remove!=null){
var v = remove.parentNode.parentNode.rowIndex;
document.getElementById(tid).deleteRow(v); (tid is the table id, not the row id)
}
However, instead of delete it, I'd just like to hide it. What's a good way to do this?
Also, in the future, I'm going to want to 'unhide' it at user request, so how can I check if it has been hidden? The if(remove!=null) is what checked if a row was already removed, so I'd need something similar.
Thank you for your time.
document.getElementById(tid).children[dataID].style.display = 'none';
You may need -1 on dataID
And block to show it again (or inline or whatever it originally was, for a div it's block).
JQuery:
$('#'+tid+':nth-child('+dataID+')').hide();
My own approach, in plain JavaScript:
function toggleRow(settings) {
// if there's no settings, we can do nothing, so return false:
if (!settings) {
return false;
}
// otherwise, if we have an origin,
// and that origin has a nodeType of 1 (so is an element-node):
else if (settings.origin && settings.origin.nodeType) {
// moving up through the ancestors of the origin, until
// we find a 'tr' element-node:
while (settings.origin.tagName.toLowerCase() !== 'tr') {
settings.origin = settings.origin.parentNode;
}
// if that tr element-node is hidden, we show it,
// otherwise we hide it:
settings.origin.style.display = settings.origin.style.display == 'none' ? 'table-row' : 'none';
}
// a simple test to see if we have an array, in the settings.arrayOf object,
// and that we have a relevant table to act upon:
else if ('join' in settings.arrayOf && settings.table) {
// iterate through that array:
for (var i = 0, len = settings.arrayOf.length; i < len; i++) {
// toggle the rows, of the indices supplied:
toggleRow({
origin: table.getElementsByTagName('tr')[parseInt(settings.arrayOf[i], 10)]
});
}
}
}
// you need an up-to-date browser (you could use 'document.getElementById()',
// but I'm also using 'addEventListener()', so it makes little difference:
var table = document.querySelector('#demo'),
button = document.querySelector('#toggle');
// binding a click event-handler to the 'table' element-node:
table.addEventListener('click', function (e) {
// caching the e.target node:
var t = e.target;
// making sure the element is a button, and has the class 'removeRow':
if (t.tagName.toLowerCase() === 'button' && t.classList.contains('removeRow')) {
// calling the function, setting the 'origin' property of the object:
toggleRow({
origin: t
});
}
});
// binding click-handler to the button:
button.addEventListener('click', function () {
// calling the function, setting the 'arrayOf' and 'table' properties:
toggleRow({
'arrayOf': document.querySelector('#input').value.split(/\s+/) || false,
'table': table
});
});
JS Fiddle demo.
References:
document.querySelector().
e.target.addEventListener().
Node.nodeType.
String.split().
As you've asked for a jQuery solution...how about
var $remove = $('#' + dataID);
if ($remove) {
$remove.closest('tr').closest().hide();
}
?

select multiple rows using control and shift key

Demo
<table id="tableStudent" border="1">
<thead>
<tr><th>ID</th><th>Name</th> <th>Class</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>John</td><td>4th</td></tr>
<tr><td>2</td><td>Jack</td><td>5th</td></tr>
<tr><td>3</td><td>Michel</td><td>6th</td></tr>
<tr><td>4</td><td>Mike</td><td>7th</td></tr>
<tr><td>5</td><td>Yke</td><td>8th</td></tr>
<tr><td>6</td><td>4ke</td><td>9th</td></tr>
<tr><td>7</td><td>7ke</td><td>10th</td></tr>
</tbody>
</table>
$('tr').on('click',function(e)
{
var objTR=$(this);
});
I have to select multiple rows using control key.
And then store Student ID in array.
How should i do using jquery Click event.
If you only want the cells to light up when the control key is pressed, this code does the trick:
var studendIds = [];
$(window).on('keydown',(function()
{
var target = $('tr'),
root = $(window),
clickCb = function(e)
{
if (!$(this).hasClass('ui-selected'))
{
$(this).addClass('ui-selected');
//add id to array
studentIds.push(+(this.cells[0].innerHTML))
}
else
{
$(this).removeClass('ui-selected');
for(var i=0;i<studentIds.length;i++)
{
if (studentIds[i] === +(this.cells[0].innerHTML))
{//remove id from array
delete studentIds[i];
break;
}
}
}
},
upCb = function(e)
{
target.off('click',clickCb);
root.on('keydown',downCb);
root.off('keyup',upCb);
},
downCb = function(e)
{
if (e.which === 17 || e.which === 16)
{//17 is ctrl, 16 is shift
root.off('keydown',downCb);
root.on('keyup',upCb);
target.on('click',clickCb);
}
};
return downCb;
}()));
Fiddle demo.
What this code does, essentially, is listen for a keydown event. If that key is the ctrl key (code 17), a click listener is attached, that will set/unset the ui-selected class if a particular row is clicked. The handler also detaches the keydown listener itself and attaches a keyup listener that sets up the event listeners back to their original states once the ctrl key is released.
Meanwhile, another listener is attached, that picks up on the keyup event. If the key (ctrl) is released, the click listener is removed, and the keydown event listener is restored.
As I said in the comments, though the code above does keep track of which ids are selected, I'd personally not do that.
Whenever you need those ids (probably on form submission, or to perform an ajax request), seeing as you have those rows marked usign a class, I'd just do this:
function assumingAjaxFunction()
{
var data = {some: 'boring', stuff: 'you might send', ids: []};
$('.ui-selected > td:first').each(function()
{
data.ids.push($(this).text());
});
console.log(data.ids);//array of ids
}
VanillaJS fiddle with shift-select support
and the code to go with it:
window.addEventListener('load',function load()
{
'use strict';
var tbl = document.getElementById('tableStudent');
window.addEventListener('keydown',(function()
{
var expr = /\bui\-selected\b/i,
key, prev,
clickCb = function(e)
{
e = e || window.event;
var i, target = (function(elem)
{//get the row element, in case user clicked on cell
if (elem.tagName.toLowerCase() === 'th')
{//head shouldn't be clickable
return elem;
}
while(elem !== tbl)
{//if elem is tbl, we can't determine which row was clicked anyway
if (elem.tagName.toLowerCase() === 'tr')
{//row found, break
break;
}
elem = elem.parentNode;//if td clicked, goto parent (ie tr)
}
return elem;
}(e.target || e.srcElement));
if (target.tagName.toLowerCase() !== 'tr')
{//either head, table or something else was clicked
return e;//stop handler
}
if (expr.test(target.className))
{//if row WAS selected, unselect it
target.className = target.className.replace(expr, '');
}
else
{//target was not selected
target.className += ' ui-selected';//set class
}
if (key === 17)
{//ctrl-key was pressed, so end handler here
return e;
}
//key === 16 here, handle shift event
if (prev === undefined)
{//first click, set previous and return
prev = target;
return e;
}
for(i=1;i<tbl.rows.length;i++)
{//start at 1, because head is ignored
if (tbl.rows[i] === target)
{//select from bottom to top
break;
}
if (tbl.rows[i] === prev)
{//top to bottom
prev = target;//prev is bottom row to select
break;
}
}
for(i;i<tbl.rows.length;i++)
{
if (!expr.test(tbl.rows[i].className))
{//if cel is not selected yet, select it
tbl.rows[i].className += 'ui-selected';
}
if (tbl.rows[i] === prev)
{//we've reached the previous cell, we're done
break;
}
}
},
upCb = function(e)
{
prev = undefined;//clear prev reference, if set
window.addEventListener('keydown',downCb,false);//restore keydown listener
tbl.removeEventListener('click',clickCb, false);//remove click
window.removeEventListener('keyup',upCb,false);//and keyup listeners
},
downCb = function(e)
{//this is the actual event handler
e= e || window.event;
key = e.which || e.keyCode;//which key was pressed
if (key === 16 || key === 17)
{//ctrl or shift:
window.removeEventListener('keydown',downCb,false);//ignore other keydown events
tbl.addEventListener('click',clickCb,false);//listen for clicks
window.addEventListener('keyup', upCb, false);//register when key is released
}
};
return downCb;//return handled
}()), false);
window.removeEventListener('load',load,false);
}, false);
This code is close to copy-paste ready, so please, at least give it a chance. Check the fiddle, it works fine for me. It passes JSlint in with fairly strict settings, too (/*jslint browser: true, white: true */), so it's safe to say this code isn't that bad. Yes it may look somewhat complicated. But a quick read-up about how event delegation works will soon turn out that delegating an event is easier than you think
This code also heavily uses closures, a powerful concept which, in essence isn't really that hard to understand either, this linked answer uses images that came from this article: JavaScript closures explained. It's a fairly easy read, but it does a great job. After you've read this, you'll see closures as essential, easy, powerful and undervalued constructs, promise
First of all, define some classes which will indicate that you have selected a table row:
tr.selected, tr.selected td {
background: #ffc; /* light-red variant */
}
Then write this jQuery event handler:
$('table#tableStudent').on('click', 'tr', function() {
if($(this).hasClass('selected')) {
// this accours during the second click - unselecting the row
$(this).removeClass('selected');
} else {
// here you are selecting a row, adding a new class "selected" into <tr> element.
// you can reverse the IF condition to get this one up above, and the unselecting case down.
$(this).addClass('selected');
}
});
In this way you have the expirence that you have selected a row. If you have a column which contains a checkbox, or something similar, you might want to do that logic inside the event listener I provided you above.
This might help DEMO:
function bindMultipleSelect(element){
var self = this;
var isCtrlDown = false;
element.on('click', 'tr', function(){
var tr = $(this);
if(!isCtrlDown)
return;
tr.toggleClass('ui-selected')
})
$(document).on('keydown', function(e){
isCtrlDown = (e.which === 17)
});
$(document).on('keyup', function(e){
isCtrlDown = !(e.which === 17)
});
self.getSelectedRows = function(){
var arr = [];
element.find('.ui-selected').each(function(){
arr.push($(this).find('td').eq(0).text())
})
return arr;
}
return self;
}
window.myElement = bindMultipleSelect($('#tableStudent'))

Javascript function changes behavior when passing element ID vs. hard coding it

I have the following Javascript that on a single mouse click in a table cell with id="freq-table" populates consecutive <input> form fields with id="searchTerm(x)" with the cell's value. It's referenced in the <body> tag as:
<body onload="populateFields()>
and <table> tag as:
<table onclick="populateFields()>
var index=0;
function populateFields(){
var ft_id = document.getElementById("freq-table");
var alltds = ft_id.getElementsByTagName("td");
for (var i in alltds) {
alltds[i].onclick = function () {
if(index==0) {
searchTerm1.value = this.innerHTML;
} else {
setThis(this.innerHTML);
}
}
}
if (index<2) {
index++;
} else {
index = 1;
}
}
function setThis(value) {
document.getElementById("searchTerm"+index).value = value;
}
When trying to make the function more universal by passing the element id (as follows), it now takes a SECOND mouse click to start populating the fields.
<table onclick="populateFields(this)" id="freq-table">
function populateFields(element){
var alltds = element.getElementsByTagName("td");
What is it about the revision that's changing the behavior? Am I just incorrectly passing the id? Or is revised function now expecting a variable to be passed to it in <body> tag? It's confusing because: If I am incorrectly passing the id, why would the function work consecutively AFTER the first mouse click? What is the fix for this, please?
You have some heavy code here, where the first table click (or body onload) sets additional click event handlers.
What you should do instead is use event delegation. With event delegation, the click event handler is attached to the table but knows which cell was clicked (the target).
[Update] Code sample based on the above article:
var index=0;
var tableIds=["freq-table1","freq-table2","freq-table3"];
for (var i=0;i<tableIds.length;i++) {
var currentId=tableIds[i];
var table=document.getElementById(currentId);
table.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
while(target != this) {
if (target.nodeName == 'TD') {
// target is our cell
setThis(target.innerHTML);
}
target = target.parentNode;
}
// increment index modulo 3
index=(index+1)%3;
}; // end of onclick function
} // end of for loop
Live demo: http://jsfiddle.net/srVmF/2/
I think the call can come from the TD or the TR element. So, the first time the id will be 'undefined'.
Why not call the function with the event and verify the tag name:
<table onclick="populateFields(event)" id="freq-table">
Javascript
function populateFields(e) {
var source = e.target || e.srcElement;
if (e.tagName == 'table') {
var ft_id = document.getElementById(source.id);
Instead of being populated on page load, now you have to click on the table before it populates the fields.
You could leave the page load handler:
<body onload="populateAllFields()">
For every table you add a class:
<table class="mytable">
Then, the code:
function populateAllFields()
{
[].forEach.call(document.getElementsByClassName('mytable'), populateFields);
}
Your <body onload="populateFields()> isn't passing the element you want, so the initial set that would be done when the page loads is no longer happening.
You can fix it by passing the ID instead, and give the onload handler the ID.
function populateFields(id){
var ft_id = document.getElementById(id);
var alltds = ft_id.getElementsByTagName("td");
// and so on...
}
<body onload="populateFields('freq-table')">
<table onclick="populateFields(this.id)">

Categories