take filtered rows in count in navigation - javascript

I'm trying to take in count filtered rows from an html table when I navigate it. I can't find any clear solutions, what would be the best way to do this please ?
Here is the filtering part of the script:
function searchPokemon() {
var input, filter, found, table, tr, td, i, j;
input = document.getElementById("mySearch");
filter = input.value.toUpperCase();
table = document.getElementById("pokemons-list");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td");
for (j = 0; j < td.length; j++) {
if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
found = true;
}
}
if (found) {
tr[i].style.display = "";
found = false;
} else {
tr[i].style.display = "none";
}
}
}
And here the table navigation part:
var rows = document.getElementById("pokemons-list").children[1].children;
var selectedRow = 0;
document.body.onkeydown = function(e){
rows[selectedRow].style.backgroundColor = "#FFFFFF";
if(e.keyCode == 38){
selectedRow--;
} else if(e.keyCode == 40){
selectedRow++;
} else if(e.keyCode == 13){
Pokemon_ID = selectedRow + 1;
alert('Pokemon_ID = ' + Pokemon_ID);
}
if(selectedRow >= rows.length){
selectedRow = 0;
} else if(selectedRow < 0){
selectedRow = rows.length-1;
}
rows[selectedRow].style.backgroundColor = "dodgerblue";
rows[selectedRow].scrollIntoView(true);
};

Below snippet is a basic example of what you're trying to do. Run the code snippet and type 'Aa' (without the quotes). Notice when you press the down arrow you immediately get the first Aa but then have to press down 3 more times before the following line gets highlighted. The following two down arrows are really on the hidden tr elements. The elements are hidden from being show to the user but they are still within the DOM itself.
Solution:
During the onkeydown event, before you change the background color to dodgerblue, verify the current tr element does not have display none. If it does, loop through again and check the next expected result. Since you have it looping through, as long as you don't loop back to beyond where you started then you shoudn't have an infinite loop.
var searchPokemon;
var rows = document.getElementById("pokemons-list").children[1].children;
var selectedRow = 0;
document.body.onkeydown = function(e) {
rows[selectedRow].style.backgroundColor = "#FFFFFF";
if(e.keyCode == 38){
selectedRow--;
} else if(e.keyCode == 40){
selectedRow++;
} else if(e.keyCode == 13){
Pokemon_ID = selectedRow + 1;
alert('Pokemon_ID = ' + Pokemon_ID);
}
if(selectedRow >= rows.length){
selectedRow = 0;
} else if(selectedRow < 0){
selectedRow = rows.length-1;
}
rows[selectedRow].style.backgroundColor = "dodgerblue";
rows[selectedRow].scrollIntoView(true);
};
searchPokemon = function() {
var input, filter, found, table, tr, td, i, j;
input = document.getElementById("mySearch");
filter = input.value.toUpperCase();
table = document.getElementById("pokemons-list");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td");
for (j = 0; j < td.length; j++) {
if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
found = true;
}
}
if (found) {
tr[i].style.display = "";
found = false;
} else {
tr[i].style.display = "none";
}
}
}
<html>
<head>
</head>
<body>
<input id="mySearch" />
<button type="button" onclick="searchPokemon()">Filter</button>
<table id="pokemons-list">
<thead> </thead>
<tbody>
<tr style="background-color: rgb(255, 255, 255);">
<td>1</td>
<td>Aaa</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>2</td>
<td>Aba</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>3</td>
<td>Abb</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>4</td>
<td>Aab</td><td>
<img src=""></td>
</tr>
</tbody>
</table>
</body>
</html>
The snippet below should be updated so allow you to do the same test, but skip the hidden tr elements.
var searchPokemon;
var rows = document.getElementById("pokemons-list").children[1].children;
var selectedRow = 0;
document.body.onkeydown = function (e) {
rows[selectedRow].style.backgroundColor = "#FFFFFF";
var startedAt;
do {
startedAt = selectedRow;
if (e.keyCode == 38) {
selectedRow--;
} else if (e.keyCode == 40) {
selectedRow++;
} else if (e.keyCode == 13) {
Pokemon_ID = selectedRow + 1;
alert('Pokemon_ID = ' + Pokemon_ID);
}
if (selectedRow >= rows.length) {
selectedRow = 0;
} else if (selectedRow < 0) {
selectedRow = rows.length - 1;
}
} while (startedAt != selectedRow && rows[selectedRow].style.display === 'none');
rows[selectedRow].style.backgroundColor = "dodgerblue";
};
searchPokemon = function () {
var input, filter, found, table, tr, td, i, j;
input = document.getElementById("mySearch");
filter = input.value.toUpperCase();
table = document.getElementById("pokemons-list");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td");
for (j = 0; j < td.length; j++) {
if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
found = true;
}
}
if (found) {
tr[i].style.display = "";
found = false;
} else {
tr[i].style.display = "none";
}
}
}
<html>
<head>
</head>
<body>
<input id="mySearch" />
<button type="button" onclick="searchPokemon()">Filter</button>
<table id="pokemons-list">
<thead> </thead>
<tbody>
<tr style="background-color: rgb(255, 255, 255);">
<td>1</td>
<td>Aaa</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>2</td>
<td>Aba</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>3</td>
<td>Abb</td><td>
<img src=""></td>
</tr>
<tr style="background-color: rgb(255, 255, 255);">
<td>4</td>
<td>Aab</td><td>
<img src=""></td>
</tr>
</tbody>
</table>
</body>
</html>

Related

How to keep colums stable while dynamically altering the display value of rows in an HTML table

I have an HTML page with a drop down menu and a table. I have a Javascript function which will filter the table depending on the value selected from the drop down. I do this by traversing the table and changing the style of each row to either display:initial or display:none. The problem is that the columns change position when I select a value from the drop down and it's making my table look wonky. Does anyone know what might be causing this and how I can get around it? Any help would be much appreciated! I'm running it in Chrome if that is a factor.
Here is the HTML:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class="container">
<label for="filter">FILTER</label>
<select name="filter" id="filter" onchange="filter(value)">
<option value="All">See All</option>
<option value="Pending">See Pending</option>
<option value="Approved">See Approved</option>
<option value="Rejected">See Rejected</option>
</select>
<table id="reimTable">
<thead>
<tr>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="tableBody">
<tr>
<th>Pending</th>
<th>$10.00</th>
</tr>
<tr>
<th>Approved</th>
<th>$2.50</th>
</tr>
<tr>
<th>Rejected</th>
<th>$23.59</th>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript" src="/Resources.js"></script>
</body>
</html>
This is the Javascript:
function filter(value){
var table = document.getElementById("reimTable");
if (value == "All"){
for (var i = 1, row; row = table.rows[i]; i++) {
row.style.display = "initial";
}
}
if (value == "Pending"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Pending"){
row.style.display = "initial";
} else {
row.style.display = "none";
}
}
}
if (value == "Approved"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Approved"){
row.style.display = "initial";
} else {
row.style.display = "none";
}
}
}
if (value == "Rejected"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Rejected"){
row.style.display = "initial";
} else {
row.style.display = "none";
}
}
}
}
Don't use inline on* attributes, same as you don't use inline style attributes
Use classList.toggle() to toggle a desires utility class.
Use data-* attributes to relate elements to a dependency value instead of going up and down the DOM (like ie: data-status="pending") directly on the TR element.
Regarding CSS, hard to say what styles you have used so far.
PS: Programming is not about copy-pasting code.
const ELS_tr = document.querySelectorAll("#tableBody tr");
const EL_filter = document.querySelector("#filter")
const filter = (ev) => {
const val = ev.currentTarget.value;
ELS_tr.forEach(EL => EL.classList.toggle("u-none", val && EL.dataset.status !== val));
}
EL_filter.addEventListener("change", filter);
.u-none {
display: none;
}
<div class="container">
<label for="filter">FILTER</label>
<select name="filter" id="filter">
<option value="">See All</option>
<option value="pending">See Pending</option>
<option value="approved">See Approved</option>
<option value="rejected">See Rejected</option>
</select>
<table id="reimTable">
<thead>
<tr>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="tableBody">
<tr data-status="pending">
<th>Pending</th>
<th>$10.00</th>
</tr>
<tr data-status="approved">
<th>Approved</th>
<th>$2.50</th>
</tr>
<tr data-status="rejected">
<th>Rejected</th>
<th>$23.59</th>
</tr>
</tbody>
</table>
</div>
I ended up using:
row.style.display = "table-row";
I think that setting it to an empty string would have worked just as well.
Thanks everyone for your help.
You can set display to an empty string. I have modified your Resources.js file.
function filter(value){
var table = document.getElementById("reimTable");
if (value == "All"){
for (var i = 1, row; row = table.rows[i]; i++) {
row.style.display = "";
}
}
if (value == "Pending"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Pending"){
row.style.display = "";
} else {
row.style.display = "none";
}
}
}
if (value == "Approved"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Approved"){
row.style.display = "";
} else {
row.style.display = "none";
}
}
}
if (value == "Rejected"){
for (var i = 1, row; row = table.rows[i]; i++) {
if (row.cells[0].innerHTML == "Rejected"){
row.style.display = "";
} else {
row.style.display = "none";
}
}
}
}

Filtering html table (JS)

I have an html table in my view that I want to filter with multiple filters. In this case, I have 3 filters, but I can have much more.
Here is a little part of the code, to show the problem
$(document).ready(function () {
$('#datefilterfrom').on("change", filterRows);
$('#datefilterto').on("change", filterRows);
$('#projectfilter').on("change", filterProject);
});
function filterRows() {
var from = $('#datefilterfrom').val();
var to = $('#datefilterto').val();
if (!from && !to) { // no value for from and to
return;
}
from = from || '1970-01-01'; // default from to a old date if it is not set
to = to || '2999-12-31';
var dateFrom = moment(from);
var dateTo = moment(to);
$('#testTable tr').each(function (i, tr) {
var val = $(tr).find("td:nth-child(2)").text();
var dateVal = moment(val, "DD/MM/YYYY");
var visible = (dateVal.isBetween(dateFrom, dateTo, null, [])) ? "" : "none"; // [] for inclusive
$(tr).css('display', visible);
});
}
function filterProject() {
var contentToColor = {
"Заявка отменена": "#9900ff",
"Подтверждено менеджером Vchasno": "green",
"Отменено менеджером Vchasno": "#9900ff",
"Отклонено региональным менеджером": "#9900ff",
"Подтверждено региональным менеджером": "red"
};
var project = this.value;
var filter, table, tr, td, i;
filter = project.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<div class="row">
<div class="col-md-3">
<h4>Дата з</h4>
<input type="date" class="form-control" id="datefilterfrom" data-date-split-input="true">
</div>
<div class="col-md-3">
<h4>Дата до</h4>
<input type="date" class="form-control" id="datefilterto" data-date-split-input="true">
</div>
<div class="col-md-2">
<h4>Проект</h4>
<select id="projectfilter" name="projectfilter" class="form-control"><option value="1">Тестовый проект</option><option value="2">Тест2</option></select>
</div>
</div>
<table id="testTable" class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Дата</th>
<th scope="col">Проект</th>
</tr>
</thead>
<tbody id="report">
<tr>
<td class="proposalId">9</td><td> 17/07/2018</td> <td> Тестовый проект</td>
</tr>
<tr><td class="proposalId">8</td><td> 18/07/2018</td><td> Тестовый проект</td></tr>
<tr><td class="proposalId">7</td><td> 17/07/2018</td><td> Тест2</td></tr>
<tr style=""><td class="proposalId">3</td><td> 19/07/2018</td><td> Тест2</td></tr>
</tbody>
</table>
Here is the full working snippet
https://codepen.io/suhomlineugene/pen/JBBGXa
When I set date filter it filters table great, but when I set the second filter it gets data from the table that I have unfiltered.
Where is my problem?
Thank's for help!
the problem was filter = project.toUpperCase() is returning 1 or 2. I updated the logic to get the innerHTML and the do compare. Here is the modified code
function filterProject() {
var contentToColor = {
"Заявка отменена": "#9900ff",
"Подтверждено менеджером Vchasno": "green",
"Отменено менеджером Vchasno": "#9900ff",
"Отклонено региональным менеджером": "#9900ff",
"Подтверждено региональным менеджером": "red"
};
let dumb = this.options.selectedIndex;
dumb = this.options[dumb].innerHTML;
console.log(dumb);
var filter, table, tr, td, i;
filter = dumb.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
}
}
Code pen link here
When you do filterProject(), check if you have already filtered out the rows you are iterating over:
....
for (i = 0; i < tr.length; i++) {
if(tr[i].style.display !== 'none'){
...
Here's a working codepen:
https://codepen.io/anon/pen/NBBxad
HTML
<input type="text" id="input_id" onkeyup="tableSearch('id', 'table', 0)">
Javascript
function tableSearch(input_id, table_id, col) {
var input, filter, table, tr, td, i;
input = document.getElementById(input_id);
filter = input.value.toUpperCase();
table = document.getElementById(table_id);
tr = table.getElementsByTagName('tr');
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName('td')[col];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}

HTML Table search single cell

I have been looking for a code to search a table and display a single cell. Kept trying to make my own and eventually found one that works perfectly except for one thing. When the search bar is emptied, it displays the first cell, I don't know how to make it so it goes back to displaying the whole table.
Here's the code and any help is really appreciated.
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
for (var i = 0; i < cells.length; ++i) {
if (cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0) {
cells.forEach(function(element) {
element.style.display = "none";
});
cells[i].style.display = "table-cell";
break;
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
}
});
<input id="myInput">
<table id="myTable">
<tr>
<td>asd</td>
<td>dsa</td>
</tr>
<tr>
<td>123</td>
<td>321</td>
</tr>
<tr>
<td>412</td>
<td>124</td>
</tr>
</table>
And here's the code in action and you can see what my problem is: https://jsfiddle.net/y1wof1go/
When nothing is entered in input box (length is zero or empty string) you need to make all cells in your table visible again.
Here is a fiddle.
and update function:
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
if (search.value.length > 0 && search.value != '') {
for (var i = 0; i < cells.length; ++i) {
if (cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0) {
cells.forEach(function(element) {
element.style.display = "none";
});
cells[i].style.display = "table-cell";
break;
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
}
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
});
<input id="myInput">
<table id="myTable">
<tr>
<td>asd</td>
<td>dsa</td>
</tr>
<tr>
<td>123</td>
<td>321</td>
</tr>
<tr>
<td>412</td>
<td>124</td>
</tr>
</table>
So to make sure the table returns to its default state when the search is empty add an if statement checking if the search bar is empty. If the bar is empty, then set all of the table elements back to showing with the normal table-cell style.
var cells = document.querySelectorAll("#myTable td");
var search = document.getElementById("myInput");
search.addEventListener("keyup", function() {
for (var i = 0; i < cells.length; ++i) {
if (cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0) {
cells.forEach(function(element) {
element.style.display = "none";
});
cells[i].style.display = "table-cell";
break;
} else {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
}
if (search.value == "") {
cells.forEach(function(element) {
if (cells[i] !== element) {
element.style.display = "table-cell";
}
});
}
});
<input id="myInput">
<table id="myTable">
<tr>
<td>asd</td>
<td>dsa</td>
</tr>
<tr>
<td>123</td>
<td>321</td>
</tr>
<tr>
<td>412</td>
<td>124</td>
</tr>
</table>
When you need to reset all items on any platform, it is often best to perform a blanket reset to the original state:
search.addEventListener('keyup', function()
{
if (search.value.length > 0 && search.value != '')
{
for (var i = 0; i < cells.length; ++i)
{
if(cells[i].textContent.toLowerCase().indexOf(search.value.toLowerCase()) === 0)
{
cells.forEach(function(element)
{
element.style.display = 'none';
});
cells[i].style.display = 'table-cell';
break;
}
else
{
cells.forEach(function(element)
{
if (cells[i] !== element)
{
element.style.display = 'table-cell';
}
});
}
}
}
else
{
cells.forEach(function(element)
{
if (cells[i] !== element)
{
element.style.display = 'table-cell';
}
});
}

Is there a way to implement on keyup event except one key?

I'm trying to develop a Javascript function that do some verification every time an input change (I'm using onkeyup event) except when we delete what we enter inside the input. Here is my actual code:
function myFunction() {
var input, filter, table, tr, td, i;
var cpt = 0;
var nbRow = 0;
input = document.getElementById("filterInput");
filter = input.value.toUpperCase();
table = document.getElementById("test");
thead = table.getElementsByTagName("tbody");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
nbRow = nbRow + 1;
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
cpt = cpt + 1;
}
}
}
if (nbRow == cpt) {
alert("The list is empty")
}
}
<input id="filterInput" onkeyup="myFunction()">
<table id="test">
<thead>
<tr>
<th>Titre1</th>
<th>Titre2</th>
<th>Titre3</th>
</tr>
</thead>
<tbody>
<tr>
<td>contenu1</td>
<td>contenu2</td>
<td>contenu3</td>
</tr>
</tbody>
</table>
How can I avoid a repetitive alert show everytime a user deletes one character?
EDIT :
I'm trying to avoid repetitive 'alert' without losing the verification after the user deletes one character.
If you want to ignore delete and backspace with onkeyup, you can add this check to the beginning of your function:
function myFunction(event) {
// capture what key was pressed
var key = event.keyCode || event.charCode;
if( key === 8 || key === 46 )
return; //if it's del or backspace, exit the function
// continue your function here
}
You can implement a validation in the function detecting which key was pressed...
function myFunction(e) {
if(e.keyCode !== 8){ // back key excluded
var input, filter, table, tr, td, i;
var cpt = 0;
var nbRow = 0;
input = document.getElementById("filterInput");
filter = input.value.toUpperCase();
table = document.getElementById("test");
thead = table.getElementsByTagName("tbody");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
nbRow = nbRow + 1;
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
cpt = cpt + 1;
}
}
}
if (nbRow == cpt) {
alert("The list is empty")
}
}
}
<input id="filterInput" onkeyup="myFunction(event)">
<table id="test">
<thead>
<tr>
<th>Titre1</th>
<th>Titre2</th>
<th>Titre3</th>
</tr>
</thead>
<tbody>
<tr>
<td>contenu1</td>
<td>contenu2</td>
<td>contenu3</td>
</tr>
</tbody>
</table>
Of course you would have to add the code for every key you want to exclude

Javascript cut table to insert a tag <div>

I want to cut table after tr to insert a div and re-open the table :
Before :
<table>
<tr onClick="cutAfter(this);">
<td>bla</td><td> 123 </td><td>Yes </td>
</tr>
<tr onClick="cutAfter(this);">
<td>bli</td><td> 456 </td><td>no</td>
</tr>
<tr onClick="cutAfter(this);">
<td>blu</td><td> 789 </td><td>hum</td>
</tr>
</table>
After :
<table>
<tr onClick="cutAfter(this);">
<td>bla</td><td> 123 </td><td>Yes </td>
</tr>
<tr onClick="cutAfter(this);">
<td>bli</td><td> 456 </td><td>no</td>
</tr>
</table>
<div onClick="fuse(this)">It works</div>
<table>
<tr onClick="cutAfter(this);">
<td>blu</td><td> 789 </td><td>hum</td>
</tr>
</table>
And return to first state on click.
Any idea (no jQuery).
A simple whay to do it is to use combination of DOM manipulation methods like insertAdjacentHTML to create new table and appendChild to move rows into new table:
function cutAfter(row) {
var table = row.parentNode.parentNode;
if (row.nextElementSibling) {
table.insertAdjacentHTML('afterend', '<table><tbody></tbody></table>');
var newTable = table.nextElementSibling.tBodies[0];
while (row.nextElementSibling) {
newTable.appendChild(row.nextElementSibling);
}
}
}
table {
margin-bottom: 10px;
}
table td {
border: 1px #AAA solid;
}
<table>
<tr onClick="cutAfter(this);">
<td>bla</td><td> 123 </td><td>Yes </td>
</tr>
<tr onClick="cutAfter(this);">
<td>bli</td><td> 456 </td><td>no</td>
</tr>
<tr onClick="cutAfter(this);">
<td>blu</td><td> 789 </td><td>hum</td>
</tr>
</table>
Here's a simple example, made of an HTML file (the structure) and a Javascript file (the behavior). The script uses node manipulation, so as to preserve existing handlers (e.g. added by other scripts). It also attaches event handlers directly, using element.onxxx, to keep things simple, but you should replace that by your favorite event manager.
var makeSplittable = function(table, joinText) {
init();
function init() {
var tBodies = table.tBodies;
for (var ii = 0; ii < tBodies.length; ii++) {
var rows = tBodies[ii].rows;
for (var j = 0; j < rows.length; j++) {
rows[j].onclick = split; // Replace with your favorite event manager
}
}
}
function split(evt) {
var rowIndex = this.rowIndex;
var tbody = findParent(this, "tbody");
var numRows = tbody.rows.length;
if (rowIndex < numRows - 1) {
var rows = [];
for (var ii = rowIndex + 1; ii < numRows; ii++) {
rows.push(tbody.rows[ii]);
}
var existingTable = findParent(this, "table");
var newTable = createTable(rows);
var joiner = createJoiner();
existingTable.parentNode.insertBefore(newTable, existingTable.nextSibling);
existingTable.parentNode.insertBefore(joiner, existingTable.nextSibling);
}
}
function createTable(rows) {
var table = document.createElement("table");
var tbody = document.createElement("tbody");
for (var ii = 0; ii < rows.length; ii++) {
tbody.appendChild(rows[ii]);
}
table.appendChild(tbody);
return table;
}
function createJoiner() {
var div = document.createElement("div");
var content = document.createTextNode(joinText);
div.appendChild(content);
div.onclick = join; // same
return div;
}
function join(evt) {
var previousTable = this.previousSibling;
var nextTable = this.nextSibling;
var tbody = previousTable.tBodies[previousTable.tBodies.length - 1];
var rows = nextTable.rows;
while (rows.length) {
tbody.appendChild(rows[0]);
}
nextTable.parentNode.removeChild(nextTable);
this.parentNode.removeChild(this);
}
function findParent(element, type) {
if (!element || !type) {
return null;
}
if (element.nodeName.toLowerCase() == type.toLowerCase()) {
return element;
}
return findParent(element.parentNode, type);
}
};
makeSplittable(document.getElementById("target"), "Merge adjacent tables");
table,
div {
margin: 5px 0;
}
tr:hover td {
background-color: orange;
}
td {
background-color: yellow;
cursor: pointer;
padding: 10px;
}
div {
color: #0c0;
cursor: pointer;
}
<table id="target">
<tr>
<td>bla</td>
<td>123</td>
<td>Yes</td>
</tr>
<tr>
<td>bli</td>
<td>456</td>
<td>no</td>
</tr>
<tr>
<td>blu</td>
<td>789</td>
<td>hum</td>
</tr>
</table>
A possibility, assuming that a DIV should not be inserted after the last TR if there has not been a cut, but it would have been nice to see your effort. Also assuming no <thead> or <tfoot>.
function isTagName(element, tagName) {
return element.tagName.toUpperCase() === tagName.toUpperCase();
}
function getClosest(element, tagName) {
var closest = null;
while (element !== document && !isTagName(element, tagName)) {
element = element.parentNode;
}
if (element !== document && isTagName(element, tagName)) {
closest = element;
}
return closest;
}
function insertAfter(newNode, referenceNode) {
return referenceNode.parentNode
.insertBefore(newNode, referenceNode.nextSibling);
}
function moveAppend(list, dest, from) {
var index = list.length - 1,
last;
for (last = from || 0; index >= last; index -= 1) {
dest.appendChild(list[index]);
}
return dest;
}
document.body.addEventListener('click', function(e) {
var target = e.target,
tr = getClosest(target, 'tr'),
newDiv,
newTable,
newBody,
next,
parent;
if (tr) {
if (tr.rowIndex < tr.parentNode.rows.length - 1) {
newDiv = document.createElement('div');
newDiv.appendChild(document.createTextNode('It works!'));
insertAfter(newDiv, getClosest(tr, 'table'));
newTable = document.createElement('table');
newBody = document.createElement('tbody');
moveAppend(tr.parentNode.rows, newBody, tr.rowIndex + 1);
newTable.appendChild(newBody);
insertAfter(newTable, newDiv);
}
} else if (isTagName(target, 'div') &&
isTagName(target.previousElementSibling, 'table') &&
isTagName(target.nextElementSibling, 'table')) {
next = target.nextElementSibling;
moveAppend(next.tBodies[0].rows, target.previousElementSibling.tBodies[0]);
parent = target.parentNode;
parent.removeChild(next);
parent.removeChild(target);
}
}, false);
table,
td {
border-style: solid;
border-width: 1px;
}
div {
background-color: yellow;
}
<table>
<tr>
<td>bla</td>
<td>123</td>
<td>Yes</td>
</tr>
<tr>
<td>bli</td>
<td>456</td>
<td>no</td>
</tr>
<tr>
<td>blu</td>
<td>789</td>
<td>hum</td>
</tr>
</table>

Categories