I am working on a project using HTML5, CSS3, Javascript and Bootstrap.
The current page I'm having issues with has an HTML Table that requires four main functionalities: add new row, move row up, move row down, delete row.
To be more specific, I need it to be able to:
Provide the user the option to add an IP range to a new row at the bottom of the table.
Provide the new IP Range with an order number that follows the previously defined order. (Example: if there are 10 IP Ranges listed, the new IP Range should have an order number of 11)
Provide user buttons in each row to move an IP Range up or down in order.
Provide the user the option to delete an IP Range.
Right now I can add IP Ranges to a new row at the bottom of the table. I can move rows up and down. I can also delete a row.
Currently my code requires me to select the row I plan to move before I move it, which is a feature I would like to remove.
What I need help with:
1. How can I remove the need to SELECT the row before doing these functionalities? Example: if the user presses the UP arrow in row 2, just move the IP Range from row 2 to row 1?
2. How can I automatically reorder the rows based on if a row is deleted? Example: if there exists an IP Range in the table, it should start with a value Order=1. Any subsequent IP Range rows 'n' should have a value of 1+n. If there are three IP Ranges in the table and the second IP Range is deleted, how can I ensure that the order will not be (1, 3) and instead (1, 2)?
Note: the information in the table in the example provided is static but I will be filling dynamically from database. I will not know beforehand how many IP Ranges there are.
code:
function SomeDeleteRowFunction() {
// event.target will be the input element.
var td = event.target.parentNode;
var tr = td.parentNode; // the row to be removed
tr.parentNode.removeChild(tr);
}
//This function adds a user-input IP Range to the end of table body
function addEndIPRanges() {
//Accessing table body
var table = document.getElementById("myTableBody");
//Insert row at end of table body (-1 appends)
var row = table.insertRow(-1);
//Insert 3 new cells into new row
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var rowCount = table.rows.length;
cell1.innerHTML = rowCount;
cell2.innerHTML = document.getElementById('addIPRange').value;
}
"use strict";
const tbody = document.querySelector("#table tbody");
let selected = null;
tbody.addEventListener("click", function(e) {
let row = e.target.closest("tr");
if (row === selected) {
row.classList.toggle("selected")
selected = null;
} else {
if (selected) {
selected.classList.toggle("selected");
}
selected = row;
row.classList.toggle("selected");
}
});
function upNdown(direction) {
let up, down;
if (selected) {
up = direction == "up" ? selected : selected.nextElementSibling;
down = direction == "up" ? selected.previousElementSibling : selected;
if (up && down) {
tbody.insertBefore(up, down); // put up before down
var temp = up.firstElementChild.textContent; // swap first cells' text content
up.firstElementChild.textContent = down.firstElementChild.textContent;
down.firstElementChild.textContent = temp;
}
}
}
tr {
cursor: pointer
}
.selected {
background-color: red;
color: #fff;
font-weight: bold
}
<figure class="text-center mb-3">
<h1 class="display-5">IP Range Management</h1>
</figure>
<hr>
<input type='text' id='addIPRange' />
<button style="text-align: center;" onclick="addEndIPRanges()">Add IP Range</button>
<hr>
<h4 class="iprangetableheader" style="text-align: center;">IP Ranges</h4>
<table id="table" style="margin: auto; width: 75%;">
<thead>
<tr style="text-align: center;">
<th scope="col">Order</th>
<th scope="col">IP Range</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody id="myTableBody">
<tr>
<td>1</td>
<td>255.255.255.255</td>
<td><button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
<tr>
<td>2</td>
<td>234.132.1.642</td>
<td><button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
<tr>
<td>3</td>
<td>24.32.2.25</td>
<td><button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
</tbody>
</table>
<hr>
Link to JS Fiddle: What I've got so far
It might look weird but it works:
You can send the clicked button as a parameter to the function and derive the row which it belongs to. That row is your required element.
const tbody = document.querySelector("#table tbody");
function upNdown(direction,button) {
// Here button parameter is getting the button which is clicked
// Its parent element is a td whose parent element is a tr which is to be moved and voila
let selected = button.parentElement.parentElement;
let up, down;
if (selected) {
up = direction == "up" ? selected : selected.nextElementSibling;
down = direction == "up" ? selected.previousElementSibling : selected;
if (up && down) {
tbody.insertBefore(up, down); // put up before down
var temp = up.firstElementChild.textContent; // swap first cells' text content
up.firstElementChild.textContent = down.firstElementChild.textContent;
down.firstElementChild.textContent = temp;
}
}
}
<table id="table" style="margin: auto; width: 75%;">
<thead>
<tr style="text-align: center;">
<th scope="col">Order</th>
<th scope="col">IP Range</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody id="myTableBody">
<tr>
<td>1</td>
<td>255.255.255.255</td>
<td><button onclick="upNdown('up',this);">↑</button>
<button onclick="upNdown('down',this);">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
<tr>
<td>2</td>
<td>234.132.1.642</td>
<td><button onclick="upNdown('up',this);">↑</button>
<button onclick="upNdown('down',this);">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
<tr>
<td>3</td>
<td>24.32.2.25</td>
<td><button onclick="upNdown('up',this);">↑</button>
<button onclick="upNdown('down',this);">↓</button>
<input type="button" value="Delete Row" onclick="SomeDeleteRowFunction()">
</td>
</tr>
</tbody>
</table>
This is how you can maintain the index:
Add a class ip-row or whatever you want to all the tr elements which hold the ip.
Remember to give that class to only the elements which hold the ip otherwise it will index all the rows.
Take that class and insert the following code in the delete function and put it after you have removed the element like this-
function SomeDeleteRowFunction() {
// event.target will be the input element.
var td = event.target.parentNode;
var tr = td.parentNode; // the row to be removed
tr.parentNode.removeChild(tr);
// Select all trs
let trs = Array.from(tbody.querySelectorAll('.ip-row'))
// Start index
let index = 1;
trs.forEach((tr) => {
// For all trs give a value to its first td which holds the index
tr.querySelectorAll('td')[0].innerHTML = index;
// increment the index
index++
})
}
Related
Basically where I have the 3 buttons 1, 2, and 3 in the second column first row if I type 3 the only button shows up is 3....removing buttons 1 and 2
for example if I'm looking for text with "2" in it it should still show
I want all buttons to stay regardless if they show up in the search or not...can this be achieved?
I have atleast 4 columns visible at all times and I want to search ONLY the text in the < TD > not the element text in the < TD > so radio buttons, buttons, check boxes....I want those to be immune from searches always show them as long as that particular row has the text snippet in one of the columns of that row just search the text in < TD >
Googling the right phrase has led me here because google assumes I want a checkbox to search a table....NO....I want a search to only focus on text not element text if that makes sense
Thanks
function myFunction() {
const input = document.getElementById("myInput");
const inputStr = input.value.toUpperCase();
const search_length = inputStr.length;
//alert(search_length);
document.querySelectorAll('#myTable tr:not(.header)').forEach((tr) => {
const anyMatch = [...tr.children]
.some(td => td.textContent.toUpperCase().includes(inputStr));
//fix the button issue here
if (anyMatch) tr.style.removeProperty('display');
else tr.style.display = 'none';
});
}
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Type to search">
<table id="myTable" border="2">
<thead><tr>
<th>Col1</th>
<th>Col2</th>
</tr></thead>
<tbody>
<tr><td>2</td><td>
<table>
<tr><td>2<button type="button">1</button></td></tr>
<tr><td>5<button type="button">2</button></td></tr>
<tr><td>9<button type="button">3</button></td></tr>
</table></td></tr>
<tr><td>test4</td><td><button type="button">5</button></td></tr>
</tbody>
</table>
If you do not want to hide the sub table rows your selector needs to only touch the outside table.
function myFunction() {
const input = document.getElementById("myInput");
const inputStr = input.value.toUpperCase();
const search_length = inputStr.length;
//alert(search_length);
document.querySelectorAll('#myTable > tbody > tr:not(.header)').forEach((tr) => {
const anyMatch = [...tr.children]
.some(td => td.textContent.toUpperCase().includes(inputStr));
//fix the button issue here
if (anyMatch) tr.style.removeProperty('display');
else tr.style.display = 'none';
});
}
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Type to search">
<table id="myTable" border="2">
<thead>
<tr>
<th>Col1</th>
<th>Col2</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>
<table>
<tr>
<td>2<button type="button">1</button></td>
</tr>
<tr>
<td>5<button type="button">2</button></td>
</tr>
<tr>
<td>9<button type="button">3</button></td>
</tr>
</table>
</td>
</tr>
<tr>
<td>test4</td>
<td><button type="button">5</button></td>
</tr>
</tbody>
</table>
I have a script that able to add new column to a HTML table. When user press add group the header will become Group1, Group2 and so on.. Currently im adding a delete group function that able to delete all the added column. So now the problem is when I delete a column and add again a new column , the name Group(x) will not be reset.
For example:user add group1 and group2 after that he delete group2. The next time he press add group again, it will show group3 instead of group2
Image:
Expected Output: The column name should be reset whenever the column is deleted.
Html:
<table id="persons" border="1">
<thead id="theadID">
<tr>
<th>Name</th>
<th>sex</th>
<th>Message</th>
</tr>
</thead>
<tbody id="tbodyID">
<tr>
<td>Viktor</td>
<td>Male</td>
<td>etc</td>
</tr>
<tr>
<td>Melissa</td>
<td>Female</td>
<td>etc</td>
</tr>
<tr>
<td>Joe</td>
<td>Male</td>
<td>etc</td>
</tr>
</tbody>
</table>
<button onclick="javascript:appendColumn()">Add column</button>
<input type="button" onclick="deleteLastColumn();" value="do it"/>
Jquery& Javascript:
function deleteLastColumn() {
$('#persons tr').find('th:last-child, td:last-child').remove()
}
let groupNum = 1;
const tableEl = document.getElementById('persons');
// append column to the HTML table
function appendColumn() {
// open loop for each row and append cell
for (let i = 0; i < tableEl.rows.length; i++) {
createCell(tableEl.rows[i].insertCell(tableEl.rows[i].cells.length), i, 'col');
}
tableEl.rows[0].querySelector('td:last-child').textContent = 'Group' + groupNum;
groupNum++;
}
// create DIV element and append to the table cell
function createCell(cell, text, style) {
var div = document.createElement('div'), // create DIV element
txt = document.createTextNode(text); // create text node
div.appendChild(txt); // append text node to the DIV
div.setAttribute('class', style); // set DIV class attribute
div.setAttribute('className', style); // set DIV class attribute for IE (?!)
cell.appendChild(div); // append DIV to the table cell
}
maybe I'm missing it but I don't see the code which deletes ALL the cols, I only see one. But still the solution seems simple, just decrement the groupNum variable when you delete a col
function deleteLastColumn() {
$('#persons tr').find('th:last-child, td:last-child').remove()
groupNum--;
}
I was working on a university project. They told us to make 2 arrays. The first will have 3 cells with 3 images, and the second will be empty with 1 row.
I need to remove the image from the cell clicked each time in the first table and copy it to the second table!
My problem is that deleteCell() function will only delete the first element each time. I don't know how to delete the CLICKED cells from my table row!
My JS:
var table1 = document.getElementById("myTable");
var table2 = document.getElementById("myTable2");
function DL1() {
var row = document.getElementById("myRow1");
row.deleteCell();
}
function CR2() {
var row = document.getElementById("myRow2");
}
My HTML:
<table id="myTable" class="auto-style1">
<tr id="myRow1">
<td onclick="DL1()"><img src="../../2.jpg" /></td>
<td onclick="DL1()"><img src="../../1.gif" /></td>
<td onclick="DL1()"><img src="../../3.png" /></td>
</tr>
</table>
<table id="my2Table">
<tr id="myRow2"></tr>
</table>
var table1=document.getElementById("myTable");
var table2=document.getElementById("myTable2");
function DL1(elem){
var row = document.getElementById("myRow1");
for(i=0;i<row.children.length;i++) {
if(row.children[i]==elem) {
row.deleteCell(i);
row2=document.getElementById("myRow2");
row2.appendChild(elem);
}
}
}
<td onclick="DL1(this)"><img src="http://placehold.it/100x100"/></td>
<td onclick="DL1(this)"><img src="http://placehold.it/150x100"/></td>
<td onclick="DL1(this)"><img src="http://placehold.it/200x100"/></td>
Demo: https://jsfiddle.net/Lt2cyw0g/2/
So, you need to get index of clicked element (pass it to the function, and check index, and use it in deleteCell() function), then add element to the second table row...
Just pass clicked element to the function:
var table1 = document.getElementById("myTable");
var table2 = document.getElementById("myTable2");
function DL1(td) {
td.parentNode.removeChild(td);
}
function CR2() {
var row = document.getElementById("myRow2");
}
<table id="myTable" class="auto-style1">
<tr id="myRow1">
<td onclick="DL1(this)">
<img src="../../2.jpg" />
</td>
<td onclick="DL1(this)">
<img src="../../1.gif" />
</td>
<td onclick="DL1(this)">
<img src="../../3.png" />
</td>
</tr>
</table>
<table id="my2Table">
<tr id="myRow2"></tr>
</table>
Hope it helps, no need ID:
var a = document.querySelectorAll("table tr");
for(var b in a){
var c = a[b];
if(typeof c == "object"){
c.onclick = function (){
this.offsetParent.deleteRow(this.rowIndex);
}
}
}
<table >
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>1a</td><td>2a</td><td>3a</td></tr>
<tr><td>1b</td><td>2b</td><td>b</td></tr>
</table>
<table>
<tr><td>a</td><td>aa</td><td>aa</td></tr>
<tr><td>b</td><td>bb</td><td>bb</td></tr>
<tr><td>c</td><td>cc</td><td>cc</td></tr>
</table>
I have a scenario where there is a table of 4 rows, in the 4th row is a textbox. When an "onchange" event of the textbox is triggered, I want to extract the data in the cells of the same specific row into another table. and ofcourse my table is consisted of more than one row.
<div class="ProductsTable">
<table class="tablestyle">
<tr>
<th>Item</th>
<th>Price</th>
<th>Preview</th>
<th class="auto-style4">Quantity</th>
<th class="auto-style15">Selected Items</th>
</tr>
<tr id="row1">
<td class="auto-style1" id="item_name">Sofa</td>
<td class="auto-style2" id="item_price">$280.00</td>
<td class="auto-style3">
<img class="itemimage" src="images\sofa1.jpg" />
</td>
<td class="auto-style4">
<input class="quantitybox" id="item_quantity" type="text" onchange="get_quantity();" />
</td>
<td rowspan="10">
<table class="InvoiceTable" id="invoice">
<tr>
<th class="auto-style7">Item</th>
<th class="auto-style2">Price</th>
<th class="auto-style4">Quantity</th>
</tr>
</table>
</td>
</tr>
</table>
</div>
And my javascript is:
function get_quantity() {
var table = document.getElementById("invoice");
var row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2)
cell1.innerHTML = document.getElementById("item_name").innerHTML;
cell2.innerHTML = document.getElementById("item_price").innerHTML;
cell3.innerHTML = document.getElementById("item_quantity").value;
}
How can I create a loop to check through all my table when an "onchange" event is triggered. As I actually have 10 rows in my table.
Preferably without using jquery.
If I understand you correctly you are looking for a function to copy the content of the changed row, that is the data in each column, into the invoice table.
I expect the rows to be created dynamicaly with some sort of iteration. When iterating I expect a unique number is availabe why this can be used as a general selector.
Here is a function to do this:
function get_quantity(rowNumber) {
var table = document.getElementById("invoice" + rowNumber);
var row = table.insertRow(1);
var columns = document.getElementById("row" + rowNumber).childNodes;
var dataColumnIndex = 0;
for (var i = 0; i < columns.length; i++) {
if (columns[i].className == "data") {
var cell = row.insertCell(dataColumnIndex);
cell.innerHTML = columns[i].innerHTML;
dataColumnIndex++;
}
}
var inputQuantity = document.getElementById("item_quantity" + rowNumber).value
row.insertCell(dataColumnIndex).innerHTML = inputQuantity;
}
You select what columns to copy by marking the them with class data.
I gets the invoice table by expecting it to have the id invoice + number. Fx. invoice1. Same goes with each row and the input field.
Here is a plunker with a full example.
Edit
There should only be a single invoice table where all products are added to.
By selecting the same table for row insertion in the function this is fixed.
This updated plunker has the change
I have a HTML like this:
<table id="laboral">
<tr>
<td><input type="text" name="start"/></td>
<td><input type="text" name="end"/></td>
<td><textarea name="desc"></textarea></td>
<td><button type="button" onclick="saveValues(this);createRow('laboral')"> + </button></td>
</tr>
</table>
What I want is to save the values in the three cells (2 inputs and 1 textarea).
The button creates another row just like the first, with the same inputs and names. The problem is that I don't know how to access THIS row, I mean, the row who owns the button.
I tried with this.parentNode.parentNode but didn't work.
Try this
<table id="laboral">
<tr>
<td><input type="text" name="start"/></td>
<td><input type="text" name="end"/></td>
<td><textarea name="desc"></textarea></td>
<td><button type="button" onclick="saveValues(this)"> + </button></td>
</tr>
</table>
var inputVals = [];
function saveValues(elm) {
// button td tr tbody table
var table = elm.parentNode.parentNode.parentNode.parentNode;
// iterating through the first row cells
for (var i = 0; i<table.rows[0].cells.length-1; i++) {
// the current cell
var cell = table.rows[0].cells[i];
// pushing the input elm's value into the array
inputVals.push(cell.childNodes[0].value);
// retrieving the pushed value
alert(inputVals[i]);
}
}
Fiddle example
You can modify the code.
You're passing a reference to the button into saveValues, so within saveValues the first argument will refer to the button. Let's call that argument btn. btn.parentNode will be the td containing the button, and `btn.parentNode.parentNode will be the tr containing that td. So:
function saveValues(btn) {
var tr = btn.parentNode.parentNode;
// Work with `childNodes` and the `childNodes` of those children to get the values
}