If element is empty hide next element - javascript

I want to be able to find a way to hide a preceding <td>'s contents if the one above it is empty. I have my table set up as such:
<tr>
<td class="firsttd">
</td>
</tr>
<tr>
<td class="nexttd">
Hide me if above TD is empty
</td>
</tr>
<tr>
<td class="firsttd">
</td>
</tr>
<tr>
<td class="nexttd">
Hide me if above TD is empty
</td>
</tr>
And so far have:
$(".firsttd").each(function( index ) {
var dotlenght = $(this).html().length;
if (dotlenght < 1){
$(this).next('.nexttd').hide();
}
});
But cannot get it to work correctly. I cannot figure our how to tell JQuery which element to target.
Can anyone point me in the right direction?

You need to use:
$(this).parent().next().find('.nexttd').hide();
|| || ^^------find td `.nexttd`
|| ^^------Traverse to next tr
^^------Traverse to parent tr
Also you do not need to iterate over elements individually. You can target all first td elements that are empty using .filter() function and can narrow down the complete code to:
$( "td.firsttd" ).filter(function(){
return $(this).html() == "";
}).parent().next().find('.nexttd').hide();

There are two issues in your code.
First is the length of the content:
$(this).html().length; // will produce 1
$.trim($(this).html()).length; // will produce 0
The second one is hiding the next row:
$(this).next().find('.nexttd').hide();
should be:
$(this).parent().next().find('.nexttd').hide();

Related

how to search for a checkbox inside innerHTML?

I want to sum a selected column inside a table. 2nd and 3rd column in my case. I managed to get the sum but I really want to add the value of a row in case a checkbox in column #1 is checked.
I can get the innerHTML value of a cell abut I do not know how to search or find out if the checkbox inside is checked or not.
console.log(cell.innerHTML);
returns for example
"Extend (2x)<input type=\"checkbox\" id=\"Extend2x\" name=\"Extend2x\" class=\"beru\" <=\"\" td=\"\">
so I can see that the checkbox is there but that is where I ended up
I tried
console.log(cell.innerHTML.getElementsByTagName("checkbox"));
console.log(cell.innerHTML.html());
console.log(cell.html());
console.log($(cell).find(':checkbox').checked) returns undefined
but nothing worked.
Could somoone help me to find out? The working fiddle is here You just click the checkbox and summing of the columns will be done.
The code you are looking for is this
$(':checked')
you can add stuff like input:checked, or something to make it more specific.
EDIT--
just saw the comments - and Taplar already answered this. Well this can be considered as alternative answer, and does not need to use cells / iterate through cells of the table.
I think this is what you wanted. This is using jQuery for every reference to elements in the the dom (html).
$(".beru").on('change', function() {
updateTotals();
});
function updateTotals() {
// loop cells with class 'celkem'
$('.celkem').each(function(){
// for each celkem, get the column
const column = $(this).index();
let total = 0;
// loop trough all table rows, except the header and the row of totals
$(this).closest('table').find('tr:not(:first, :last)').each(function(){
if($(this).find('input').is(':checked')) {
// if the input is checked, add the numeric part to the total
const str = $(this).find(`td:eq(${column})`).text().replace(/\D/g, "");
if(str) {
total += Number(str);
}
}
});
if(!total) {
// if the total is zero, clear the cell
$(this).text("");
} else {
// otherwise, print the total for this column in the cell
$(this).text(total + " EUR");
}
});
}
td {
width: 25%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="Zinzino" style="border-collapse: collapse; width: 100%;" border="1">
<tbody>
<tr>
<th><strong>Název</strong></th>
<th class="sum"><strong>První balíček</strong></th>
<th class="sum"><strong>Měsíčně</strong></th>
<th> </th>
</tr>
<tr>
<td>BalanceOil <input type="checkbox" id="BalanceOil" name="BalanceOil" class="beru"></td>
<td>149 EUR</td>
<td>30 EUR</td>
<td> </td>
</tr>
<tr>
<td>Extend (2x)<input type="checkbox" id="Extend2x" name="Extend2x" class="beru"</td>
<td>44 EUR</td>
<td>22 EUR</td>
<td> </td>
</tr>
<tr>
<td>Zinobiotic (3x)<input type="checkbox" id="Zinobiotic" name="Zinobiotic" class="beru"</td>
<td>64 EUR</td>
<td>23 EUR</td>
<td> </td>
</tr>
<tr>
<td><strong>Celkem</strong></td>
<td class="celkem"> </td>
<td class="celkem"> </td>
<td> </td>
</tr>
</tbody>
</table>
If you already have a reference to the DOM element then just select all the checkboxes using a selector.
var cbList = cell.querySelectorAll("[type='checkbox']");
for(var i = 0; i < cbList.length; i++){
//do something with each checkbox
//cbList[i];
}
Remember a node list is not an array, so you can't use forEach ;)

D3/CSS Dom upward traversal/selection

I have the following HTML structure to represent a calendar:
<table>
<thead>...</thead>
<tbody>
<tr>...</tr>
<tr>
<td day="4">...</td>
<td day="5">...</td>
<td day="6" class="is-startrange">...</td>
<td day="7">...</td>
<td day="8">...</td>
</tr>
<tr>
<td day="9">...</td>
<td day="10">...</td>
<td day="11">...</td>
<td day="12">
<button class="day" type="button">12</button>
</td>
<td day="13">...</td>
</tr>
</tbody>
</table>
My question is: starting from the button under day 12, how can I traverse up, select all the button elements until a is-startrange class is encountered?
Each table cell is a button representing a date and listeners have been added to all the button elements. When a date is clicked, I will get the selected date as starting point.
I want to add style to all the button elements between the start date and selected date (either add class or through pure CSS).
Is there a way this can be achieved in D3 selection or pure CSS?
As Gerardo Furtado already mentioned in his comment the question is not actually about traversing the DOM upwards, but rather about an iteration of td elements. This can easily be done by using d3.selectAll("td") which will yield a flattened selection of all tds found on the page. Depending on your layout you might need to further narrow the selection down to a specific table which could be done by adjusting the selector to "table.myTable td", "#tableId td" or the like.
Having this selection at hand you can apply a class, say range, by using selection.classed(names[, value]) which can take a function passed in as the second argument value:
If the value is a function, then the function is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element. The function’s return value is then used to assign or unassign classes on each element.
The only task left is to implement a filter function which keeps track, if an element is within the desired or range or not and, thus, determines whether to assign the range class.
The following snippet shows how this could all be put together using a filter function rangeFilter() provided to .classed():
// The day parameter determines the stop criterion
function rangeFilter(day) {
// This property is closed over by the following function to keep track of the
// range. If this is true, this element and following elements belong to the
// range until this property becomes false again once reaching the button's td.
var inRange = false;
// Filter function returning true, if the element belongs to the range.
return function(d) {
element = d3.select(this); // The actual td element of this iteration step.
// Evaluate if the element is still in the range or, in case the range has not
// yet started, check if we reached the td.is-startrange.
inRange = (inRange && element.attr("day") != day)
|| element.classed("is-startrange");
// XOR to exclude the .is-startrange element.
return inRange != element.classed("is-startrange");
}
}
d3.selectAll("button")
.on("click", function() {
// For all tds check if they belong to the range and set the class based
// on the result of the filter function passing in this buttons value.
d3.selectAll("td")
.classed("range", rangeFilter(d3.select(this).text()));
});
.is-startrange {
background-color: limegreen;
}
.range {
background-color: red;
}
<script src="https://d3js.org/d3.v4.js"></script>
<h1>Hit the button</h1>
<table>
<thead>...</thead>
<tbody>
<tr>...</tr>
<tr>
<td day="4">...4...</td>
<td day="5">...5...</td>
<td day="6" class="is-startrange">...6...</td>
<td day="7">...7...</td>
<td day="8">...8...</td>
</tr>
<tr>
<td day="9">...9...</td>
<td day="10">...10...</td>
<td day="11">...11...</td>
<td day="12">
<button class="day" type="button">12</button>
</td>
<td day="13">...13...</td>
</tr>
</tbody>
</table>

Adding event handlers to each table row that will affect only that table row?

This isn't actually something I'm currently attempting to do; it just occurred to me while working with another table that I have no idea how I'd go about doing it, and the entire time on the train ride home I was puzzling over different solutions, none of which I could imagine working.
Imagine this situation: there is a table of 50 rows. On each row is a button. When clicked, this button should do something to the row it's on -- say, make all its text strikethrough, or make an AJAX call with the first cell's value, or something.
How would you go about binding those event handlers?
My initial thought was something like
buttons = document.getElementsByTagName("input");
rows = document.getElementsByTagName("tr");
for (i=0;i<buttons.length;i++) {
buttons[i].addEventListener('click',function() {
makeAjaxCall(rows[i]);
});
};
That is,
For each button in the document,
Add an event handler to that button,
Which will makeAjaxCall with the data of the row whose number corresponds to the button.
The problem, of course, being that makeAjaxCall will check the value of i when it's invoked, by which time, i is equal to buttons.length, and so the function will only ever work on the final row of the table.
So I suppose you'd need to find some way to actually hard-code the current value of i within the function handler... and that's something I don't even think is possible. Is it? How would you actually do something like this?
You can refer to the button object you are adding the event listener to using 'this'
Given the table in this format
<table>
<tr>
<td> <input type='button'> </td>
<td> 1st <td>
</tr>
<tr>
<td> <input type='button'> </td>
<td> 2nd <td>
</tr>
<tr>
<td> <input type='button'> </td>
<td> 3rd <td>
</tr>
</table>
The following code will allow you to access the data in the next cell, shown using console.log() rather than any ajax calls of course.
buttons = document.getElementsByTagName("input");
for (i=0;i<buttons.length;i++) {
buttons[i].addEventListener('click',function() {
makeAjaxCall(this);
});
};
function makeAjaxCall(btn) {
var sib=btn.parentNode.nextSibling;
while (sib.nodeName !='TD') {
sib=sib.nextSibling;
}
console.log(sib.innerHTML);
}
This can be extended to find any data in the row.
This section
while (sib.nodeName !='TD') {
sib=sib.nextSibling;
}
skips any extraneous characters (white space etc) between cells.
Fiddle Here
Interesting problem. I would go about doing it the way #kennebec suggests in the comment above.
Here is the fiddle: http://jsfiddle.net/u988D/
First, a slight change in markup to add data attributes
HTML
<table>
<tr>
<td data-to-json="id">1</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
<tr>
<td data-to-json="id">2</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
<tr>
<td data-to-json="id">3</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
</table>
Then the javascript. Probably can be optimized a bit more.
JS
var table = document.querySelector("table")
table.addEventListener("click", function(e) {
var element = e.target,
parent
//If the element is a button
if ( element && element.nodeName == "BUTTON" ) {
parent = element.parentNode
//Find the closest parent that is a TR
while ( parent.nodeName !== "TR" ) {
parent = parent.parentNode
}
//Convert Row to JSON
var json = {},
child
for ( var i = 0, _len = parent.children.length; i < _len; i++ ) {
child = parent.children[i]
if ( child.hasAttribute("data-to-json") ) json[child.getAttribute("data-to-json")] = child.innerText
}
// Do your AJAX stuff here
console.log(json)
}
})

Hide row if it contains empty columns

I have a table with a couple of rows, each row with two columns, the first column will hold title and second column will have the respective values.sometimes, cells in the right may not have values, so it doesn't make sense to have just the title..with no value.. I can either hide the title on the left cell that has no value on the right or the whole row itself.
I have come up with this but its not working..
$('.EventDetail tr').each(function(){
if(!$('td:not(:empty)',this).length)
$(this).hide();
});
Here is the table. I am wondering if tag is making any difference. OR one of the has a class and the other one don't ..should that be causing it to not work?
<table cellpadding="10" class ="EventDetail">
<tr>
<td class="TableFields"><em>Who Should Enroll?:</em></td>
<td>Everyone 18 and older who would like to attend</td>
</tr>
<tr>
<td class="TableFields"><em>Handicapped Access:</em></td>
<td>Yes</td>
</tr>
<tr>
<td class="TableFields"><em>Parking Notes:</em></td>
<td></td>
</tr>
<tr>
<td class="TableFields"><em>Instructor:</em></td>
<td>John Filler</td>
</tr>
</table>
So there is no parking notes info, so I want to hide the left cell that contains the title 'Parking Notes".
I think this will work:
$('.EventDetail tr').has('td:nth-child(2):empty').hide()
You can try it on jsFiddle.
Try this:
$('.EventDetail tr').each(function(){
if ($('td:empty',this).length > 0))
$(this).hide();
});
Your selector will cause the if statement never to be true for any row in your example. $("td:not(:empty)") always selects the <td> element with the title, so length is always 1. if(!1) is never true.
You should remove the double negative (the ! and the :not) to make it clearer, and then check that the length (i.e. number of matched elements) is > 0.
You can try this:
$(document).ready(function(){
$('.EventDetail tr').each(function(){
if ( $(this).children().not('.TableFields').text().length == 0 )
$(this).hide();
});
});

Combining Rows in Javascript

I'm looking for some help on the Javascript angle of this problem. I have a table that goes like...
<table>
<tbody>
<tr> (Row 1)
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr> (Row 1a)
<td>
<select option>
</td>
</tr>
<tr> (Row 2)
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr> (Row 2a)
<td>
<select option>
</td>
</tr>
<tr>
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr>
<td>
<select option>
</td>
</tr>
<tbody>
</table>
There are actually more like 20 rows and row a's but I didn't think I'd want to copy them all.
I basically need to add a container row (a single row) around every two rows (# and #a). Something like:
<tr> (Container Row 1)
<td>
+<tr> (Row 1)
+<tr> (Row 1a)
</td>
</tr>
It needs to cycle through the whole table. Somehow it has to retain the HTML data inside since all of the "a"s have options.
I hope this makes sense...
Any help would be greatly appreciated, as I'm at a loss. I'm novice at best at javascript and am struggling my way through the DOM and TOM methods.
Thank you so much in advance for any help or headway.
[EDIT] For clarification, the table is already constructed from a third party database, I am editing it after it's constructed. I guess this clarifies why it would have to be javascript to be done through the DOM.
Embed another table:
<tr> (Container Row 1)
<td>
<table>
<tr><td>(Row 1a)</td></tr>
<tr><td>(Row 1b)</td></tr>
</table>
</td>
</tr>
Or if you are wanting to do that via Javascript, you can give the parent <td> an id and set it's innerHTML.
<tr> (Container Row 1)
<td id='rowX'>
</td>
</tr>
document.getElementById('rowX').innertHTML = "<table><tr><td>(Row 1a)</td></tr><tr><td>(Row 1b)</td></tr></table>";
As mentioned in another answer you can't add tr elements directly in td like you are trying.
You would first create an inner table.
If you were using jQuery you would do something like this:
//setup some click actions just to prove that they remain attached even after moving
$('#outterTable tr').click(function(){
alert('You clicked on row: '+$(this).text());
});
//update the table (group each even row with the one after it)
$('#outterTable tr:even').each(function() {
var $tr1 = $(this),
$tr2 = $tr1.next('tr'),
$t = $('<table></table>');
$('<tr></tr>').append($t).insertBefore($tr1);
//click actions will remain attached
//if that is not required, than use $tr1.remove()
$t.append($tr1).append($tr2);
});​
See this live jsFiddle example.
without jQuery it may look like that:
<script type="text/javascript">
<!--
function fx(table)
{
var tmp=document.createElement('table');
tmp.appendChild(document.createElement('tbody'))
while(table.rows.length)
{
if(table.rows.length%2==0)
{
var wrapper=tmp.lastChild.appendChild(document.createElement('tr'));
wrapper.appendChild(document.createElement('td'));
wrapper.getElementsByTagName('TD')[0].appendChild(document.createElement('table'));
wrapper.getElementsByTagName('TD')[0].lastChild.appendChild(document.createElement('tbody'));
}
wrapper.getElementsByTagName('TD')[0].lastChild.lastChild.appendChild(table.getElementsByTagName('TR')[0])
}
table.parentNode.replaceChild(tmp,table);
tmp.setAttribute('border',1);
}
window.onload=function(){fx(document.getElementsByTagName('table')[0]);}
//-->
</script>
Example#jsFiddle
But: why do you need this grouping?
If the only benefit is a visible grouping I would prefer to do this by setting the borders of the cells .
Give all cells a border and to the even a border-top:none / to the odd a border-bottom: none

Categories

Resources