Prevent adding duplicate values html table - javascript

I have one HTML table and I don't want the value of 2nd column to repeat in the grid.
Here is my jQuery:
$('#tb_cartTable tr td:nth-child(2)').each(function() {
$ele=$(this).text();
if($ele==$productCode)
{
flag="x";
return false;
}
});
if($rowCounter>0 && flag=="x")
{
alert("Duplicate");
}
else
{
//...
}

One way will be to "map" the text of the cells into a JavaScript complex array having the text as key, then compare the amount of keys to amount of cells. If more cells than keys it means there are cells with the same text.
Code for this:
var allCells = $('#tb_cartTable tr td:nth-child(2)');
var textMapping = {};
allCells.each(function() {
textMapping[$(this).text()] = true;
});
var count = 0;
for (var text in textMapping)
count++;
if (count !== allCells.length) {
alert("found duplicate values");
} else {
alert("no duplicates found");
}
Live test case.
Note the above is case sensitive: if there is a cell with "hello" and cell with "Hello" those will be considered different and it will think there are no duplicates. If case doesn't matter fix is simple case of changing the line to:
textMapping[$(this).text().toLowerCase()] = true;
Updated test case where it ignores case.
In your specific case you can store all the added values in plain array then check the array using the jQuery inArray() method:
var $addedProductCodes = [];
$("#button_addItem").click(function(event)
{
$("span.errorText").remove();
$(".errorField").addClass("notErrorField");
//Change background color of textbox to normal
$("#frmRegisterForm :input[type='text']").attr('class','notErrorField');
$hasError = false;
$ele = $(event.target);
if($ele.is("input[type=button]"))
{
$td_productCode1=$("#td_productCode1").val();
var index = $.inArray($td_productCode1, $addedProductCodes);
if (index >= 0) {
alert("You already added this product code in line #" + (index + 1));
} else {
$text_productDescription= $("#text_productDescription").val();
$text_basicDealerPrice = $("#text_basicDealerPrice").val();
$('#table_viewContent').append("<tr><td>"+$text_productDescription+"</td><td>"+$td_productCode1+"</td><td><td>"+$text_basicDealerPrice+"</td><td><input type='button' name='deleteRow' id='btn_deleteRow' value='Delete' id='deleteItem' class='deleteItem button-red'></td></tr>");
$addedProductCodes.push($td_productCode1);
}
}
});
Updated fiddle where adding same product code will give alert and won't insert.

Related

Search bar doesn't display table after one search

I can't figure out why after I search for something and then delete that search query to search something else the table doesn't display anything but is still searching for the right answer.
So in the jsfiddle if you type 001 then delete that to try to look for 002 the table that was displayed doesn't show up anymore. Any help would be greatly appreciated!
let rows = document.querySelector("#Test").rows;// or much precise query if needed
function check(event) {
let caps = event.target.value.toUpperCase();
let hyphen = caps.replace(/[-]/g, ""); // Edit : caps + hyphen will work
// Browse all rows to check if input matching and hide/show
// Fortunately row.textContent yields all texts values :)
for(let i = 1; i < rows.length; i++) {
let row = rows[i];
// test caps length to allow revert to all hidden when no text to match
if(caps.length && (row.textContent.indexOf(caps) >= 0 || row.textContent.indexOf(hyphen) >= 0)) { row.style.display = "";} // process next row
else{
row.style.display = 'none'; // Hide row (avoid an else)
}
}}
document.getElementById('myInput').addEventListener('keyup', check, true);
https://jsfiddle.net/jx8vw64L/9/
Get rid of the
caps.length &&
in your if
if((row.textContent.indexOf(caps) >= 0 || row.textContent.indexOf(hyphen) >= 0))
{
row.style.display = "";
} // process next row
else
{
row.style.display = 'none'; // Hide row (avoid an else)
}
Do you html markup like this
function textChange() {
//DO SOME CODE
}
<input type="text" name="foo" onKeyUp="return textChange()" />
OR WITH JQUERY
$('.target').change(function() {
//DO SOME CODE
});

Javascript/Jquery filters on HTML table is highly inefficient

By inefficient I mean, activating the code makes the page hang for 20+ seconds.
To set the scene, I currently have an HTML table that looks like the following. It can be fairly big, easily 1,000-1,500 rows and 40 columns wide. It is generated from Python/Flask as a static HTML table and then javascript takes over to allow the users to filter out and sort rows. I do use the jquery tablesorter widget to allow users to sort rows by whatever column they wish.
The table itself has a structure like:
<table id="myTablePlayers" class="tablesorter table table-striped table-bordered table-hover" style="overflow: visible">
<thead>
<tr>
<th>...</th>
<th>...</th>
<th>...</th>
<th>...</th>
...
<th>...</th>
</tr>
</thead>
<tbody>
<tr class="playerData">
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
...
<td>...</td>
</tr>
...
</tbody>
</table>
The filters that users are given are as follows:
minimum GP - input field that removes all rows less than the user's input in a specific column
teams - select field (text) that removes all rows that does not match in a specific column
position - select field (text) that removes all rows that does not match in a specific column
age - input field that removes all rows less than the user's input in a specific column (e.g. if enters 20, it will keep all rows with the age in the range [20.0, 21.0)
The javascript/jquery I have written and is likely the culprit is as follows:
function autoRank() {
// auto number
rank = 0;
$("#myTablePlayers .playerData").each(function() {
if ($(this).css("display") != "none") {
rank++;
$(this).find('td').eq(colRank).text(rank);
}
});
}
function filterTable() {
// Need some error checking on input not number
minGP = $("#mingp").val()
teams = $("#teamFilter").val()
position = $("#position").val()
age = $("#age").val()
$("#myTablePlayers .playerData").show();
$("#myTablePlayers .playerData").each(function() {
toHide = false;
if (teams != "") {
if ( !$(this).find('td').eq(colTeam).text().toUpperCase().includes(teams.toUpperCase())) {
toHide = true;
}
}
if ( Number($(this).find('td').eq(colGP).text()) < minGP ) {
toHide = true;
}
if (position != "") {
if (position == "D") {
if ($(this).find('td').eq(colPos).text().indexOf("D") == -1) {
toHide = true;
}
} else if (position == "F") {
if ($(this).find('td').eq(colPos).text().indexOf("D") != -1) {
toHide = true;
}
} else if ( $(this).find('td').eq(colPos).text() != position) {
toHide = true;
}
}
if (age != "") {
column = Number($(this).find('td').eq(colAge).text())
age = Number(age)
if ( column < age || column >= age+1 ) {
toHide = true;
}
}
if (toHide == true) {
$(this).hide();
}
});
autoRank();
}
$("#teamFilter").on('change', filterTable);
$("#mingp").on('change', filterTable);
$("#position").on('change', filterTable);
$("#age").on('change', filterTable);
What is the inefficiency in this code that makes the browser hang? What should I be changing to make it efficient?
I looked at Google but jquery table filter plug ins do not give me the ability to filter rows based on specific columns based on specific inputs as outlined above (e.g. https://www.sitepoint.com/12-amazing-jquery-tables/).
Currently your code works like this:
iterate all rows
then for each row:
successively for each not-empty filter, look for all its child columns
then isolate the involved column and get its value
Only regarding the above exposed mechanism and using some numbers you cited it means that, with a unique simple filter like "teams" you actually touch 40000 columns (1000 rows * 1 filter * 40 columns).
But if 2 filters are not-empty it immediately grows to 80000 columns touched, and so on.
This is obviously a first realm where to find a way to work faster, with a pretty simple change like this:
iterate all rows
then for each row:
look for all its child columns
successively for each not-empty filter, then isolate the involved column and get its value
The involved part of code becomes:
$("#myTablePlayers .playerData").each(function() {
var toHide = false,
columns = $(this).find('td');
if (teams != "") {
if ( !columns.eq(colTeam).text().toUpperCase().includes(teams.toUpperCase())) {
toHide = true;
}
}
// ...same for next filters
This way, we already get rid of multiplying the column touches by the number of not-empty filters.
But we can go further...
In the current situation, each execution actually touches all the cells of the table, while at most 4 columns are involved (for the 4 filters). So we might try to find a way to reduce the total number of touched columns from 40000 to 4000!
This can be achieved by affecting a distinguishing class (say the filter-name) to these involved columns, so we might change the code like this:
$("#myTablePlayers .playerData").each(function() {
var toHide = false,
classTeam = '.colTeam',
classGP = `.colGP`,
classPos = `.colPos`,
classAge = `.colAge`;
if (teams != "") {
if ( !$(classTeam, this).text().toUpperCase().includes(teams.toUpperCase())) {
toHide = true;
}
}
// ...same for next filters
Maybe there is an issue with this:
It is generated from Python/Flask as a static HTML table
which means you don't have hand on the generated table.
If so, you can merely add the following to affect the class names just after the page load:
$(document).ready(function() {
$("#myTablePlayers .playerData").each(function() {
$('td', this).eq(colTeam).addClass(classTeam);
$('td', this).eq(colGP).addClass(classGP);
// ...
}
}
But actually it might be improved another way (then the previous suggestion becomes useless), using a totally different method.
Since the table is static, we can act just after the page load (so only once) to prepare the needed data for a more direct access when filtering happens.
We can pre-register all the involved columns for each filter:
$(document).ready(function() {
var teamCols = $(),
GPCols = $(),
posCols = $(),
ageCols = $();
$("#myTablePlayers .playerData").each(function() {
var columns = $(this).find('td');
teamCols.add(columns.eq(colTeam));
GPCols.add(columns.eq(colGP));
posCols.add(columns.eq(colPos));
ageCols.add(columns.eq(colAge));
}
}
Then the filter can process directly addressing the involved columns. BTW we can also immediately hide their parent (this was already possible in the original version):
if (teams) {
teamCols.each(function() {
if (!this.innerHTML.toUpperCase().includes(teams.toUpperCase())) {
$(this).parent().hide();
}
}
}
This is written a bit quickly, so it might contain some flaws, and also might be yet improved...
You can improve your code by caching your re-used elements, and doing less iterations. Currently, you do three iterations over the records that can be condensed into one.
Here's how I would do it (untested):
$(function () {
// cache the elements that you will be re-using
var $minGP = $("#mingp"),
$team = $("#teamFilter"),
$position = $("#position"),
$age = $("#age");
var $rows = $("#myTablePlayers .playerData");
function filterTable() {
// Need some error checking on input not number
var minGP = $minGP.val(),
team = $team.val(),
position = $position.val(),
age = $age.val();
// set the rank as we loop (instead of having a separate iteration for ranking)
var rank = 0;
// when you use show() and .each(), you are iterating over all the rows twice
// instead loop once
$rows.each(function() {
// cache current row as it will be re-used
var $row = $(this);
// set flag to show or hide
var display = true;
if (teams != "" &&
!$row.find('td').eq(colTeam).text().toUpperCase().indexOf(teams.toUpperCase()) > -1) {
display = false;
}
if (Number($row.find('td').eq(colGP).text()) < minGP) {
display = false;
}
if (position != "") {
if (position == "D" &&
$row.find('td').eq(colPos).text().indexOf("D") == -1) {
display = false;
} else if (position == "F" &&
$row.find('td').eq(colPos).text().indexOf("D") != -1) {
display = false;
} else if ($row.find('td').eq(colPos).text() != position) {
display = false;
}
}
if (age != "") {
column = Number($row.find('td').eq(colAge).text())
age = Number(age)
if (column < age || column >= age + 1) {
display = false;
}
}
// hide or show row
$tr.toggle(display);
// set rank number
if (display) {
$row.find('td').eq(colRank).text(rank++);
}
});
}
// attach change event handler
$minGP.add($team).add($position).add($age).change(filterTable);
});
This might speed up your code by few seconds, but ultimately it depends on how much and how big your data is.

Array index swaps when its not supposed to

I currently have a dynamically generated checkbox which when clicked creates a table. Multiple selections can be selected on my checkbox and I store this in an array. I have a loop for storing different selections e.g 1st, 2nd and 3rd checked selections.
Checkbox
option_one
option_two
options_three
The problem I am facing is, if I select the options in ascending order like 1>2>3 the tables are generated correctly:
Table_One
Table_two
Table_Three
However if I I select in a descending order, or I mix it up, option_one always ends up being passed as the first selection even when this isn't true.
My code:
$(document).on("change", ".tbl_list", function () {
var tbls = [];
$("input:checkbox[name='tbl[]']:checked").each(function () {
tbls.push($(this).val());
var tbl2Name = " ";
var tbl3Name = " ";
var tblName = " ";
for (i = 0; i < tbls.length; i++) {
if (i === 0) {
tblName = tbls[i];
}
if (i == 1) {
tbl2Name = tbls[i];
}
if (i == 2) {
tbl3Name = tbls[i];
}
}
$("#table").html(tblName);
Where tbl_list is the checkbox class-name and tbl[] is the name. So why does tblName always assume the value of the first object in the checkbox list?
Lets break this down a bit, every time one of your checkboxes changes you create a brand new array
$(document).on("change", ".tbl_list", function () {
var tbls = [];
Then you loop through all the checked checkboxes filling this list
$("input:checkbox[name='tbl[]']:checked").each(function () {
tbls.push($(this).val());
jquery .each will iterate over all the checkboxes which are checked, it does this in the order they appear in the DOM, not in the order you happen to have checked them in (why would it? how would it know?)
If you want to preserrve the order that you checked them in then you need to maintain a persistant list, and push a new item to the end of the list when checked, and remove an item when unchecked.
$(".checkbox").change(function() {
if(this.checked) {
//push into array
else {
//remove from array
}
});
Cant you just check which checkbox has been selected and add its value to an array this way you can keep the order.
After the suggestions I made a few amendments and it now works as intended.
var tbls = [];
$(document).on("change", ".tbl_list", function () {
if (this.checked){
tbls.push($(this).val());
console.log(tbls);
var tbl2Name = " ";
var tbl3Name = " ";
var tblName = " ";
for (i = 0; i < tbls.length; i++) {
if (i === 0) {
tblName = tbls[i];
}
if (i == 1) {
tbl2Name = tbls[i];
}
if (i == 2) {
tbl3Name = tbls[i];
}
}
}
$("#table").html(tblName);
window.sessionStorage.setItem("tblN", tblName);
window.sessionStorage.setItem("tblN2", tbl2Name);
window.sessionStorage.setItem("tblN3", tbl3Name);

Check duplicate column data using Jquery filter

Notice that my first two table Data (Apple and Orange) are set to read-only. And I do have function for dynamic adding of row.
see this FIDDLE FOR DEMO
If the user click the Save button, all input field that detect duplicate of data from database or last duplicate of data from row which dynamically added, border-color will change to red.
$("#save").off("click").on("click",function(){
var $rows = $('#myTable tbody tr:not(:hidden)');
$rows.each(function(){
var $id = $(this).find('td input[type=\'text\']');
alert($id.val());
//Im stuck here, for checking column data is duplicate.
});
});
Im looking forward on using Jquery filter , like this :
$( "li" ).filter( "DUPLICATE DATA()" ).css( "border-color", "red" );
I am assuming you want to target the "Name" column, although you could easily change this to target all columns.
Basically, you want to go through the input elements, keeping a reference to the values you've already reviewed:
$("#save").off("click").on("click",function(){
var existing = [];
var duplicates = $('#myTable td:nth-child(3) input').filter(function() {
var value = $(this).val();
if (existing.indexOf(value) >= 0) {
return $(this);
}
existing.push(value);
});
duplicates.closest('tr').css('background-color', 'red');
});
JSFiddle
Edit: To avoid marking a read-only line as a duplicate, the process is a little less straightforward
$("#save").off("click").on("click",function(){
// Clear status of all elements
$('#myTable tr').css('background-color', 'none');
// Get all values first (Apple, Orange, etc) as strings
var allValues = $('#myTable td:nth-child(3) input').map(function() {
return $(this).val();
}).toArray();
// Iterate unique values individually, to avoid marking a read-only input as duplicate
var processedValues = [];
for (var i = 0; i < allValues.length; i++) {
var value = allValues[i];
if (value != '' && processedValues.indexOf(value) >= 0) continue;
var inputs = $('#myTable td:nth-child(3) input').filter(function() { return $(this).val() == value; });
// Check if this value is in one of the readonly
var readOnlyInput = inputs.filter(function() { return $(this).is('[readonly]'); });
if (readOnlyInput.length) {
inputs.not(readOnlyInput).closest('tr').css('background-color', 'red');
} else {
inputs.not(':first').closest('tr').css('background-color', 'red');
}
processedValues.push(value);
}
});
Updated JSFiddle

Delete specific text from form when clicked

Basically I want to make a function that, when the text is clicked, it prints the 'id' on the form. and when it's clicked again, only that 'id' is deleted (prior clicked/printed 'id's remain).
The print script I have so far:
function imprime01(obj) {
document.form2.text.value = document.form2.text.value + obj.title;
}
the div
<div onclick="imprime01(this);" title="240 ">240</div>
<div onclick="imprime01(this);" title="230 ">230</div>
<div onclick="imprime01(this);" title="220 ">220</div>
So what I want is: when I click 240, 230 it prints "240 230" on the form, and when I click "240" again, it deletes only "240" from the form. Is there a way to achieve this?
There are many ways to do this.
I would store your ids in an array. In your click handler, test for the existence of the id in your array and remove it if it exists, otherwise add it. Then write all the ids in the array to your text box:
var idList = [];
function imprime01(obj) {
var id = obj.title;
var idIndex = idList.indexOf(id);
if (idIndex > -1) {
idList.splice(idIndex, 1);
}
else {
idList.push(id);
}
document.form2.text.value = idList.join(" ");
}
This may be a little more involved than a simple string replacement, but it gives you other functionality that could be useful later. For example, if another part of your program needs to know which ids have been selected, they are already available in an array.
Edit: Rather than storing the array in a variable, you could generate it on the fly in your click handler with string.split(" "):
function imprime01(obj) {
var id = obj.title;
var idList = document.form2.text.value.split(" ");
var idIndex = idList.indexOf(id);
if (idIndex > -1) {
idList.splice(idIndex, 1);
}
else {
idList.push(id);
}
document.form2.text.value = idList.join(" ");
}​
Working demo: http://jsfiddle.net/gilly3/qWRct/
See http://jsfiddle.net/BdEMx/
function imprime01(obj) {
var arr=document.form2.text.value?document.form2.text.value.split(' '):[];
var i=arr.indexOf(obj.title);
if(i===-1){
arr.push(obj.title);
}else{
arr.splice(i,1);
}
document.form2.text.value = arr.join(' ');
}
You shouldn't add a space at the end of title attributes only because you want to join some of them.
Use replace and indexOf functions:
var str = document.form2.text.value;
if(str.indexOf(obj.title) != -1)
document.form2.text.value = str.replace(obj.title,"");
else
document.form2.text.value = str + obj.title;

Categories