JavaScript highlight table cell on tab in field - javascript

I have a website laid out in tables. (a long mortgage form)
in each table cell is one HTML object. (text box, radio buttons, etc)
What can I do so when each table cell is "tabbed" into it highlights the cell with a very light red (not to be obtrusive, but tell the user where they are)?

Use jQuery to make your life easier, and you can do something like this:
$('#mytableid input').focus( function() {
$(this).addClass('highlight');
}).blur( function() {
$(this).removeClass('highlight');
});
This is basically saying when any input element in your table is under focus add the "highlight" class to it, and once it loses focus remove the class.
Setup your css as:
input.highlight { background-color: red; }
and you should be set.

This is the table I tested my code on:
<table id="myTable">
<tr>
<td><input type="text" value="hello" /></td>
<td><input type="checkbox" name="foo" value="2" /></td>
<td><input type="button" value="hi" /></td>
</tr>
</table>
Here is the code that worked:
// here is a cross-browser compatible way of connecting
// handlers to events, in case you don't have one
function attachEventHandler(element, eventToHandle, eventHandler) {
if(element.attachEvent) {
element.attachEvent(eventToHandle, eventHandler);
} else if(element.addEventListener) {
element.addEventListener(eventToHandle.replace("on", ""), eventHandler, false);
} else {
element[eventToHandle] = eventHandler;
}
}
attachEventHandler(window, "onload", function() {
var myTable = document.getElementById("myTable");
var myTableCells = myTable.getElementsByTagName("td");
for(var cellIndex = 0; cellIndex < myTableCells.length; cellIndex++) {
var currentTableCell = myTableCells[cellIndex];
var originalBackgroundColor = currentTableCell.style.backgroundColor;
for(var childIndex = 0; childIndex < currentTableCell.childNodes.length; childIndex++) {
var currentChildNode = currentTableCell.childNodes[childIndex];
attachEventHandler(currentChildNode, "onfocus", function(e) {
(e.srcElement || e.target).parentNode.style.backgroundColor = "red";
});
attachEventHandler(currentChildNode, "onblur", function(e) {
(e.srcElement || e.target).parentNode.style.backgroundColor = originalBackgroundColor;
});
}
}
});
There are probably things here you could clean up, but I whipped this together quickly. This works even if there are multiple things in each cell.
This would be much easier, it should go without saying, if you used a library to assist you in this work - jQuery and MochiKit are the two I favor, though there are others that would work just as well.
Between the time I started writing this answer and the time I posted it, someone posted code that shows how you would do something like this in jQuery - as you can see, much shorter! Although I love libraries, I know some people either can't or will not use a library - in those cases my code should do the job.

Possibly:
<script type="text/javascript">
//getParent(startElement,"tagName");
function getParent(elm,tN){
var parElm = elm.parentNode;
while(parElm.tagName.toLowerCase() != tN.toLowerCase())
parElm = parElm.parentNode;
return parElm;
}
</script>
<tr><td><input type="..." onfocus="getParent(this,'td').style.backgroundColor='#400';" onblur="getParent(this,'td').style.backgroundColor='';"></td></tr>

Related

Why is one button overriding my other button even with unique classes?

I am new to javascript/jquery and have been stuck on this problem for a while. So I have two buttons, a clear button that will clear all forms in a row of a table and a reset button that holds all initial values for each row of the table.
The issue: So currently when I run the script the reset button will keep overriding the clear button. Meaning when I click on clear it will also act as a reset instead of clearing the row. I tried creating unique classes (.clear_button, .reset_button) to be called as you see here. I find it hard to troubleshoot javascript especially being new to it so why is this happening?
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$(".clear_button").click(function(){
function my_clearFunction(i) {
document.getElementById("id_form-" + (i - 1) + "-Name").value = " ";
document.getElementById("id_form-" + (i - 1) + "-Start").value = "";
document.getElementById("id_form-" + (i - 1) + "-End").value = "";
document.getElementById("id_form-" + (i - 1) + "-Advanced").value = " ";
}
});
$(".reset_button").ready(function(){
$('#reset :input:not([type="button"])').each(function(idx, ele) {
ele.dataset.initvalue = ele.value;
});
$('#reset [type="button"]').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
// restore the initial value
ele.value = ele.dataset.initvalue;
})
});
});
});
</script>
Note: I understand the code is not uniform, for example, my clear button logic was not written in jquery. Sorry I couldn't attach a jsfiddle, this project is relatively pretty big and I use django to import my forms so it was difficult to set up. So any input would be greatly appreciated since I have been stuck on this for quite some time and can't seem to get it. It's also worth mentioning my input tags for the buttons so here they are.
<input type="button" class="clear_button" onclick="my_clearFunction({{ forloop.counter }})" value=" x ">
<input type="button" class="reset_button" value=" x ">
when I click on clear it will also act as a reset instead of clearing the row.
Your reset listener is declared as
$('#reset [type="button"]').on('click', function(e) {
...
})
It seems the #reset element contains both clear and reset buttons, so clicking in either will restore the initial values.
The clear button, also, has two handlers of its own. There is one declared in code which in turn declares a function (that isn't called in the handler itself) and an inline handler that tries to invoke said function. That shouldn't work, for it isn't visible from the global scope.
Instead of
$(button).on('click',(e)=>{
function doSomethingWith(i) {
...
}
doSomethingWith(e.target.id);
})
If should be
function doSomethingWith(i) {
...
}
$(document).ready(function(){
$(button).on('click',(e)=>{
doSomethingWith(e.target.id);
});
});
then it would be visible for the handler but also on the global scope, so you could call it using the inline "onclick"
<button onclick="doSomethingWith({{ forloop.counter }})" >
However, you shouldn't have an inline handler if you're also declaring one in JS. Since you're dealing with the reset button in code, stick to that approach for the clear button too.
Now, the approach you follow to clear a row needs for you to know the row relative index, and the inputs on each row, for which you compute their respective ids. Whereas, when it comes to reset the original values, you don't need to know anything:
$('.reset_button').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
// restore the initial value
ele.value = ele.dataset.initvalue;
})
});
The button needs only know it's inside the same <tr> element as other inputs whose value needs to be restored. It doesn't care about the index, the IDs, not even what inputs are in place, as long as they aren't buttons.
You should do the same to clear the values:
$('.clear_button').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
ele.value = "";
});
});
When it comes to storing the original value I'm also used to resort to jQuery.data . Anyway, for this use case you can perfectly stick to
input.dataset.initialValue = input.value
Instead of
$(input).data('initialValue',input.value)
As long as you keep in mind these approaches are not interchangeable. You can't set the initialValue with dataset then get it with jQuery.data or the other way around.
function randomTime() {
return [
Number(100 * Math.random() % 12).toFixed(0).padStart(2, '0'),
Number(100 * Math.random() % 60).toFixed(0).padStart(2, '0')
].join(':');
}
function addFormRow(player_name = 'N/A') {
let tr = $('<tr class="form_row">'),
name = $('<input type="text" name="name" class="name">'),
start = $('<input type="time" name="start" class="start">'),
end = $('<input type="time" name="end" class="end">'),
advanced = $('<input type="number" name="advanced" class="advanced">'),
clear = $('<button class="clear_button">Clear</button>'),
reset = $('<button class="reset_button">Reset</button>');
name.val(player_name);
start.val(randomTime());
advanced.val(parseInt(Math.random() * 100, 10))
end.val(randomTime());
for (let input of [name, start, end, advanced, clear, reset]) {
$('<td>').append(input).appendTo(tr);
}
tr.appendTo('#forms tbody');
}
addFormRow('player one');
addFormRow('player two');
addFormRow('player three');
$(document).ready(function() {
$('#forms tbody tr').each((index,tr)=>{
$(tr).find('input').each((idx,input)=>{
$(input).data('initialValue',$(input).val());
});
})
$(".clear_button").on('click', (e) => {
let $this = $(e.target),
tr = $this.closest('tr');
tr.find('input').each((index, input) => {
input.value = '';
});
});
$(".reset_button").on('click', (e) => {
let $this = $(e.target),
tr = $this.closest('tr');
tr.find('input').each((index, input) => {
$(input).val($(input).data('initialValue'));
});
});
});
.advanced {
width: 4em;
}
.name {
width: 9em;
}
.start,
.end {
width: 5.5em;
}
.form_row input {
height: 1.1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="forms">
<thead>
<tr>
<th>name</th>
<th>start</th>
<th>end</th>
<th>advance</th>
<th colspan="2">actions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Your $(".clear_button").click() handler isn't doing anything. It defines a local function, but never calls it.
Instead of trying to call the function from onclick(), add a data attribute to buttons holding the index. Then the click handler can get this attribute and use it to find all the related elements that it needs to clear.
$(".clear_button").click(function() {
var i = $(this).data("rel-id");
document.getElementById("id_form-" + (i - 1) + "-Name").value = " ";
document.getElementById("id_form-" + (i - 1) + "-Start").value = "";
document.getElementById("id_form-" + (i - 1) + "-End").value = "";
document.getElementById("id_form-" + (i - 1) + "-Advanced").value = " ";
});
<input type="button" class="clear_button" data-rel-id="{{ forloop.counter }}" value=" x ">

Long text visualization on table headers

I'm trying to make some html with css pages (I'm new to both) and I want to add styles to a table header with column long names, this names are required to be long, but the column width is fixed, making these columns without css would show them with text wrapping so the row height would show just NOT nice. I'm trying to find a nice way to display the table. What comes to my mind, is "cut" the name of the column veryLongColumName to veryLong... and on hover of the table header, display it as they are originally, how can I do this? do I need to begin learning JavaScript >> jQuery in order to do this? Any sample code I can use?
If you have a better idea about how to display nicely that content it is welcome.
I have no idea how to do this as it seems that there need to be data manipulation (so I need JavaScript or jQuery); how ever, I think that having two div tags with the shortened name and the original name in another and display one or other depending on the mouse hover will do the work but I don't know how to make that happen (is this also jQuery?).
Thanks in advance.
http://jsfiddle.net/byakku/Q5V98/1/
I tried to implement it in plain javascript without any lib/plugin and below is what I have,
Solution using plain javascript (without jQuery) DEMO
The above demo code can be reduced a lot by using jQuery,
Solution using jQuery - DEMO
Using jQuery:
function shortHandTableHeaders(tableID, limit) {
var ths = $('#' + tableID + ' thead tr th');
var content;
ths.each (function () {
var $this = $(this);
content = $this.text();
if (content.length > limit) {
$this.data('longheader', content);
$this.text (shortHandHeaderTxt(content, limit));
$this.hover (
function() {
$(this).text($this.data('longheader'));
},
function () {
$(this).text(shortHandHeaderTxt($this.data('longheader'), limit));
}
);
}
});
}
function shortHandHeaderTxt(txt, limit) {
return txt.substring(0, limit - 3) + "...";
}
Below is the another implementation without jQuery,
function shortHandTableHeaders(tableID, limit) {
var tableEl = document.getElementById(tableID);
var thead = tableEl.getElementsByTagName("thead");
var thtrs = thead[0].getElementsByTagName("tr");
var ths, content;
for (var i = 0; i < thtrs.length; i++) {
ths = thtrs[i].getElementsByTagName("th");
for (var j = 0; j < ths.length; j++) {
content = ths[j].innerHTML;
if (content.length > limit) {
ths[j].title = content;
ths[j].innerHTML = shortHandHeaderTxt(content, limit);
addEventHandler(ths[j], 'mouseover', function() {
this.innerHTML = this.title;
});
addEventHandler(ths[j], 'mouseout', function() {
this.innerHTML = shortHandHeaderTxt(this.title, limit);
});
}
}
}
}
function addEventHandler(el, eType, handler) {
if (el.addEventListener) { // W3C, FF
el.addEventListener(eType, handler, false);
} else if (el.attachEvent) { // IE
el.attachEvent('on' + eType, function() {
handler.call(el);
});
}
}
function shortHandHeaderTxt(txt, limit) {
return txt.substring(0, limit - 3) + "...";
}
Here's a way to do it with just CSS.
demo
In the HTML, put a class="short" on the abbreviated message.
<table>
<thead>
<th>Veeery long text title for a column header</th>
<th class="short">Veeery long...</th>
</thead>
<tbody>
<tr>
<td>What it looks like when mouse hover</td>
<td>What it looks like when mouse is out of header, height should fit the content</td>
</tr>
</tbody>
</table>
Our CSS overloads the display property. There are much nicer ways to do this, but they aren't compatible with most IE versions. I haven't tested this on more than FireFox, but it should work for IE7+, and modern browsers.
thead th {
display: none;
}
thead th.short {
display: table-cell;
}
thead:hover th {
display: table-cell;
}
thead:hover th.short {
display: none;
}

Passing IDs of table rows

the following question might be an easy one for most of you. But I am just beginning JavaScript and I really would appreciate if someone could point me in the right direction.
I have a normal HTML table where each row has its own id. What I want to achieve is that each time I click a link in a row the id for that row should be stored in a variable and later passed to a function. Let's say I click row#1 then id#1 should be passed and so on... How can I achieve this?
Thank you.
This should work:
var currentRowID, table = document.getElementById('your-table');
var rows = table.rows;
var row_count = rows.length;
for(var i = 0; i < row_count; i++) {
rows[i].onclick = function() {
currentRowID = this.id;
//do something with currentRowID here...
};
}
jsFiddle example
When you arrange for an event handler to respond to clicks, the browser will set things up so that you can figure out what element was clicked (the "target" of the event). In your Javascript, you could do something like this, assuming your <table> is the only table on the page:
function handleRowClicks(e) {
e = e || window.event;
if (!e.target.tagName.toLowerCase() === "tr") return;
var rowId = e.target.id;
/*
whatever you want to do goes here
*/
}
// To set up the event handler, do this in your "window.onload" or some
// other initialization point
document.getElementsByTagName("table")[0].onclick = handleRowClicks;
This is only one of a bunch of different approaches. If you were to use a Javascript framework/library, it would get a little simpler, probably, but not much.
Note that this approach handles the clicks at the level of the <table> instead of the rows themselves. That saves a little bit of initialization work.
Similar to the other example, here's how you could do this in pure JavaScript:
// Pure JavaScript:
var table = document.getElementById("table-one");
var rows = table.rows;
var length = rows.length;
for(var i = 0; i < length; i++) {
rows[i].onclick = function() {
alert(this.id);
// Do more stuff with this id.
}
}
In my opinion, this particular problem is solved very nicely by jQuery. If you're performing other operations similar to this, jQuery will save you a ton of time, and keep the complexity level of your code down. Compare the above with this:
// jQuery:
$("#table-one tr").bind("click", function() {
alert(this.id);
});
bind is a jQuery method that just, well, binds an event to a handler :)
Working sample: http://jsfiddle.net/andrewwhitaker/9HEQk/
The easiest way to do that is using jQuery framework.
// Your function
function MyFunc(rowId){
alert(rowId);
}
// Binde click event to all rows with class "ClockableRow"
// within table with class "clickableTable"
$(".clickableTable .clickableRow").click(function(){
// Call you function and pass clicked row ID there
MyFunc($(this).attr("id"));
})
You table shoul look like this:
<table class="clickableTable">
<tr id="row1" class="clickableRow"><td>row 1</td></tr>
<tr id="row2" class="clickableRow"><td>row 2</td></tr>
<tr id="row3" class="clickableRow"><td>row 3</td></tr>
<tr id="row4" class="clickableRow"><td>row 4</td></tr>
<tr id="row5" class="clickableRow"><td>row 5</td></tr>
</table>
example

Change id of a Table row using javascript in IE

Hello I have a table which has a few rows. And I have an add button in my application which allows to add rows at any rowIndex. I use insertRow method to insert rows at a specific position. So once a row in inserted, I change the table row Ids of all the rows, to arrange them in an ascending order. Now I have been developing this application in FF and it has been coming pretty well. Now I am making a few changes to the code to make it work in IE. But this just does not work in IE. I have been testing this code for the last two days, it works in FF and chrome, but not in IE. I am not sure, what is the mistake I am making. I am just recreating the situation with an example, and this is the code for that example. Please help me out and tell me what could be the mistake I am making for it not to be working in IE. Any suggestions would be a great help.
<html>
<head>
<script type = 'text/javascript'>
function getIds()
{
var elem = document.getElementsByTagName("tr");
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
alert(elem[i].id);
}
}
function changeIds()
{
var elem = document.getElementsByTagName("tr");
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
{ index = Number(elem[i].rowIndex)+1; elem[i].id = "tabsf_"+index;}
}
alert('change');
}
</script>
</head>
<body>
<table id="tabsf">
<tbody>
<tr id="tabsf_1"><td>1</td><td>2</td></tr>
<tr id="tabsf_2"><td>3</td><td>4</td></tr>
<tr id="tabsf_5"><td>5</td><td>6</td></tr>
<tr id="tabsf_3"><td>7</td><td>8</td></tr>
<tr id="tabsf_4"><td>9</td><td>10</td></tr>
</tbody>
</table>
<table><tr><td><input type="button" name="hach" value="getIds" onclick="getIds()" /></td>
<td><input type="button" name="hach" value="Change Ids" onclick="changeIds()" /></td></tr></table>
</body>
</html>
</code>
The problem is that for..in loop doesn't ensure order. It is one of the gotchas why you should avoid them for arrays. (there is another, see my link)
Use simple for loops instead ( for i..n )
function getIds() {
var elem = document.getElementsByTagName("tr");
for (var i = 0; i < elem.length; i++) {
if (elem[i].id) {
alert(elem[i].id);
}
}
}
function changeIds() {
var elem = document.getElementsByTagName("tr");
for (var i = 0; i < elem.length; i++) {
if (elem[i].id) {
index = Number(elem[i].rowIndex) + 1;
elem[i].id = "tabsf_" + index;
}
}
alert('change');
}
Works perfectly cross-browser.
I would say try using the setAttribute function vs the id property to change the id.
Update: Try #Spinon's idea first.
Update: It seems to in fact have been an ID collision, something IE rightly prevented from happening and the other browsers happily let through.
Original answer:
My guess is that your loop creates an id collision: An id is set twice while the loop runs. IE could (rightly) be blocking the setting of a duplicate ID.
I would try unsetting the IDs first:
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
elem[i].id = elem[i].id + "_temp";
}
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
{ index = Number(elem[i].rowIndex)+1;
elem[i].getElementsByTagName("td")[0].innerHTML = "tabsf_"+index;
elem[i].id = "tabsf_"+index;}
}
However, I don't understand how you use row IDs to change the sorting?

finding object in Javascript

I have a form with thousands of checkboxes, and when one is checked, I want to check all the boxes below it.
This works:
<html>
<body>
<form name="myform">
<input type="checkbox" name="box1" onClick="redrawboxes(this);">1<br>
<input type="checkbox" name="box2" onClick="redrawboxes(this);">2<br>
...
</form>
</body>
</html>
<script>
function redrawboxes(obj){
//check all boxes below
var foundit=false;
for (e=0; e<document.myform.elements.length; e++){
if (foundit==false){ //search for checked obj
if(obj == document.myform.elements[e]){
foundit=true;
}
}else{ //continuing below checked box
if(obj.checked){ //are we checking or unchecking
document.myform.elements[e].checked = true;
}else{
document.myform.elements[e].checked = false;
}
}
}
}
</script>
but for more than a few thousand boxes, IE is unacceptably slow. (Firefox works fine.)
Is there a better way to find the original box besides iterating through the whole list?
Both of the jQuery suggestions are pretty good. For DOM wrangling like this, you're really better off using a good library.
And the comment about the dubious wisdom of putting thousands of checkboxes on a form is pretty good as well...
But, on the off-chance that you do have a good reason for doing this, and you can't use jQuery or similar, here's a fast, straight JS method:
function redrawboxes(obj)
{
//check all boxes below
var next = obj;
while ( (next = next.nextSibling) )
{
if ( next.nodeName.toLowerCase() == "input"
&& next.type.toLowerCase() == "checkbox" )
next.checked = obj.checked;
}
}
tested in FF3, FF3.1, IE6, Chrome 1, Chromium 2
i might get down voted for this, but try using jquery. it has selectors optimized for that.
Advertising inside !
If you are using jQuery, you can try my plugin to make your loop asynchronous, this will allow to run a long loop without freezing the browser.
http://mess.genezys.net/jquery/jquery.async.php
If you don't want to use jQuery, you can download the plugin and modify the code for your own needs, it does not really depend on jQuery.
You can read out the name of the selected checkbox like this:
function redrawboxes(obj) {
var name = obj.name;
var state = obj.checked;
// get that index
var index = name.substr(3) * 1; // just to be sure it's a number
var length = document.myform.elements.length;
var allElements = document.myform.elements
// (un)check all elements below
for (var i = index; i < length; i++) {
allElements[i].checked = state;
}
}
You could have sped up your code quite a bit by using local variables and there's an if-statement that can be replaced.
Edit: Actually that one-off-error isn't an error because that specific checkbox was (un)checked by the user himself.
Dunno how fast it is, but you could try the jQuery-way, grab jQuery from www.jquery.com and insert the following code on the page:
$(function(){
$("input:checkbox").click(function(){
$(this).nextAll("input:checkbox").each(function(){
this.checked = true;
});
});
});

Categories

Resources