Im trying to create a site that has any number of rows in a table that contain up to 2 inputs. I would like the combination of the inputs to be unique. That is, if we have the following elements
<select><option value='0'>None</option><option value='1'>1</option><option value='2'>2</option><option value='3'>3</option></select>
<select><option value='0'>None</option><option value='4'>4</option><option value='5'>5</option><option value='6'>6</option></select>
Then the any combinations of the two can be used 0-1 times. So, if 1 and 4 are used in one row, if 1 is selected again, 4 should not be allowed to be selected. This should work in the other direction as well. If 4 is selected then 1 should not be allowed to be selected again.
Rows can be added or removed at anytime by the user, so these checks will have to be performed every time a element is changed.
Additional Info
- The contents of the select elements are generated by a database, they are not static.
- They will always have a "value" attribute that contains an integer.
- I keep a copy of the unaltered select elements in the 'masters' div, these are used to add more rows to the table (and where the PHP dumps the options from the database). These "masters" are cloned and added in when the "Add" button is clicked.
- A value of 0 for each is allowed, but only once. This is the only guaranteed option that will appear in both select elements
Things Ive tried
Detecting the change event and trying to adjust all the select elements to enforce the unique-ness, but I couldn't quite hack this. I'm pretty sure this is the way to go, but am struggling with the way to implement it.
Trying to remove possible duplicates at the add row time, but you can't decide what is a duplicate until at least one selection is made.
Removing any row(s) that duplicate another row's combination, but this doesn't work as expected and is not user friendly anyway (this is the current implementation in the example below).
Things that don't work
How to select only Unique combinations of select dropdowns in table row (Very similar, but no working solution)
Prevent Multiple Selections of Same Value (the select elements do not contain the same items)
Extra Challenge
The other half of this is that either the projects column and/or the activities column may be hidden if there are no projects/activities in the database. In this case, the default value is 0, but it dramatically changes the expected behavior of the page and adds a bunch of edge cases to the mix. I left it out of the main question since I thought it would add too much confusion, but it's something else I'll need to deal with. This functionality is second to the main problem above, however.
Minimum Reproducible Example:
You should be able to copy-past this directly to a .html file for experimentation. I left some styling in to make it easier to understand what is needed.
jQuery.fn.outerHTML = function() {
return jQuery('<div />').append(this.eq(0).clone()).html();
};
var projects = $("#projectsMaster");
var activities = $("#activitiesMaster");
var numRows = 0;
$(document).ready(function() {
$(".entryTable").on("click", ".deleteButton", function() {
if (numRows > 0) {
var row = $(this).closest("tr");
row.remove();
numRows--;
}
});
$(".entryTable").on('change', 'select', function() {
enforceUniqueProjectsActivities(this)
});
});
function enforceUniqueProjectsActivities(select) {
var usedCombos = new Array();
$(".entryTable").find(".dataRow").each(function() {
var activity = $(this).find(".activity").find("select").val();
if (!activity) {
activity = 0;
}
if (!project) {
project = 0;
}
var project = $(this).find(".project").find("select").val();
usedCombos.push(project + "," + activity);
});
//I was planning on going through each row and removing duplicates, but this isnt very user-friendly.
$(".entryTable").find(".dataRow").each(function() {
var activity = $(this).find(".activity").find("select").val();
if (!activity) {
activity = 0;
}
var project = $(this).find(".project").find("select").val();
if (!project) {
project = 0;
}
for (var i = 0; i < usedCombos.length; i++) {
if (i === $(this).index() - 1) {
continue;
}
if (project === usedCombos[i].split(",")[0] && activity === usedCombos[i].split(",")[1]) {
$(this).remove();
}
}
});
}
function addRow() {
numRows++;
var newRow = "<tr class='dataRow' '" +
"'><td class='project'>" + projects.clone().outerHTML() + "</td><td class='activity'>" + activities.clone().outerHTML() +
"</td><td><button class='deleteButton'>Delete</button></td></tr>";
$(".entryTable").find('tr:last').prev().after(newRow);
}
.entryTable {
margin: auto;
width: 95%;
border: 1px solid black;
border-collapse: collapse;
}
.entryTable input,
select {
width: 100%;
font-size: 18px;
}
.entryTable tr {
border-bottom: 1px solid black;
border-color: black
}
.entryTable td {
text-align: center;
padding: 10px;
border-right: 1px solid black;
}
#addRow {
background-color: darkgray
}
.masters {
display: none;
}
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div class="masters">
<select id="projectsMaster">
<option value='0'>None</option>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<select id="activitiesMaster">
<option value='0'>None</option>
<option value='4'>4</option>
<option value='5'>5</option>
<option value='6'>6</option>
</select>
</div>
<br/>
<table class="entryTable" id-">
<tr>
<th>Project</th>
<th>Activity</th>
<th></th>
</tr>
<tr id="addRow">
<td></td>
<td></td>
<td style='text-align: center'><button onclick="addRow()">Add</button></td>
</tr>
</table>