I am currently trying to learn about backtracking Algorithms and have been working on a Sudoku game. I understand the basic working of the algorithm and have written a Sudoku solver using it.
My current problem is related to removing a set amount of numbers from a completed Sudoku grid to create a valid Sudoku with a unique solution.
I have checked similar questions on here but have found no answers.
Here is an example of a solved Sudoku grid:
solvedSudokuGrid =
[["8","6","1","3","4","2","9","5","7"],
["2","5","3","8","7","9","4","6","1"],
["4","9","7","6","5","1","2","3","8"],
["6","7","2","5","1","8","3","9","4"],
["9","1","4","7","2","3","6","8","5"],
["5","3","8","4","9","6","7","1","2"],
["3","4","6","2","8","5","1","7","9"],
["7","8","9","1","3","4","5","2","6"],
["1","2","5","9","6","7","8","4","3"]];
Here is the function to remove a set amount of numbers from the grid:
function removeNrs(grid, nrsToBeRemoved) {
//check if enough numbers have been removed
if (nrsToBeRemoved <= 0) {
return true;
}
//find the next random full cell and set the grid to "" on that cell to remove a number
var nextNr = shuffle(findFullCells(grid))[0];
var row = nextNr[0];
var column = nextNr[1];
var value = grid[row][column];
grid[row][column] = "";
nrsToBeRemoved--;
//check if the sudoku grid has only 1 solution if yes start recursion
if (countAllSolutions(grid) < 2){
if(removeNrs(grid, nrsToBeRemoved)){
return grid;
}
}
//if the sudoku grid has more than 1 possible solution return the grid to the previous state and backtrack
grid[row][column] = value;
return false;
}
Here is the problem: If I enter a low amount of numbers to be removed the function works.
ex:
removeNrs(solvedSudokuGrid, 5); //returns a valid grid
If I enter a higher amount of numbers to be removed the function simply returns false.
ex:
removeNrs(solvedSudokuGrid, 50); //returns false
From the basic debugging that I have tried I can see that the function works as long as it does not have to backtrack. If the function has to backtrack it seems to return all the way to the beginning and finish with the original grid before returning false.
Any help, explenations or things to read are much appreciated.
Edit:
https://jsfiddle.net/mg57u0mv/
Here is the complete code but some of the names of functions and variables have been changed to fit better with the whole code.
function createTable() {
var tbl = document.createElement("table");
var tbdy = document.createElement("tbody");
for (var row = 0; row < 9; row++) {
var tr = document.createElement("tr");
for (var column = 0; column < 9; column++) {
var td = document.createElement("td");
var input = document.createElement("input");
input.type = "text";
input.id = "r"+row+"c"+column;
input.className = "grid-inputs grid-inputs-row-" + row;
//input.placeholder = "[" + row + " , " + column + "]";
//input.placeholder = input.id;
if ((row+1) % 3 === 0) {
td.style.borderBottom = "3px solid black";
}
if ((column+1) % 3 === 0) {
td.style.borderRight = "3px solid black";
}
tr.appendChild(td);
td.appendChild(input);
}
tbdy.appendChild(tr);
}
tbl.appendChild(tbdy);
document.body.appendChild(tbl);
}
function createButton(text, func) {
var button = document.createElement("button");
var t = document.createTextNode(text);
button.onclick = func;
button.appendChild(t);
document.body.appendChild(button);
}
function shuffle(array) {
var counter = array.length;
var temp, index;
while (counter) {
index = Math.floor(Math.random() * counter);
counter--;
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
function retrieveGrid() {
var result = [];
var rowContents = [];
for (var row = 0; row < 9; row++) {
for (var column = 0; column < 9; column++) {
rowContents.push(document.getElementsByClassName("grid-inputs-row-"+row)[column].value);
}
result.push(rowContents);
rowContents = [];
}
return result;
}
function printGrid(grid) {
for (var row = 0; row < 9; row++) {
for (var column = 0; column < 9; column++) {
document.getElementsByClassName("grid-inputs-row-"+row)[column].value = grid[row][column];
}
}
}
function checkRowColumnBlock(grid, row, column, value) {
//create row, column and block lists to be checked for doubles
var rowList = grid[row];
var columnList = [];
for (var columnCounter = 0; columnCounter < 9; columnCounter++) {
columnList.push(grid[columnCounter][column]);
}
var blockList = [];
for (var startRow = Math.floor(row/3) * 3, endRow = startRow + 3; startRow < endRow; startRow++) {
for (var startColumn = Math.floor(column/3) * 3, endColumn = startColumn + 3; startColumn < endColumn; startColumn++) {
blockList.push(grid[startRow][startColumn]);
}
}
//check row, column and block list for value
if (rowList.indexOf(value.toString()) === -1 &&
columnList.indexOf(value.toString()) === -1 &&
blockList.indexOf(value.toString()) === -1) {
return true;
} else {
return false;
}
}
function checkGrid(grid) {
for (var row = 0; row < 9; row++) {
for (var column = 0; column < 9; column++) {
if (grid[row][column] !== "") {
var value = grid[row][column];
grid[row][column] = "";
if (!checkRowColumnBlock(grid, row, column, value)) {
console.log("Invalid Grid");
return false;
}
grid[row][column] = value;
}
}
}
console.log("Valid Grid");
return true;
}
function findEmptyCells(grid) {
var result = [];
for (var row = 0; row < 9; row++){
for (var column = 0; column < 9; column++) {
if (grid[row][column] === "") {
result.push([row , column]);
}
}
}
if (result.length == 0) {
result = false;
}
return result;
}
function sortPossibilties(grid) {
var result = [];
var listOfEmptyCells = findEmptyCells(grid);
if (listOfEmptyCells === false) {
return false;
}
var listOfPossibilities = findPossibilitiesForGrid(grid);
var counter = listOfEmptyCells.length;
for (var cell = 0; cell < counter; cell++) {
result.push({"cell": listOfEmptyCells[cell], "possibilities": listOfPossibilities[cell]});
}
result.sort(function (first, second) {
return first.possibilities.length - second.possibilities.length;
});
return result;
}
function findNextEmptyCell(grid) {
var sortedEmptyCells = sortPossibilties(grid);
if (sortedEmptyCells === false) {
return false;
}
return sortedEmptyCells[0];
}
function findFullCells(grid) {
var result = [];
for (var row = 0; row < 9; row++){
for (var column = 0; column < 9; column++) {
if (grid[row][column] !== "") {
result.push([row , column]);
}
}
}
if (result.length == 0) {
result = false;
}
return result;
}
function findRandomFullCell(listOfFullCells) {
if (listOfFullCells === false) {
return false;
}
var result = listOfFullCells[Math.floor(Math.random() * listOfFullCells.length)];
return result;
}
function createEmptyGrid() {
//create grid 9x9 fill with blankspace
var grid = [];
for (var gridCounter = 0; gridCounter < 9; gridCounter++) {
grid.push(new Array(9).fill(""));
}
return grid;
}
function createIncRandomGrid(numberOfRandomCells) {
var grid = createEmptyGrid();
for (var counter = 0; counter < numberOfRandomCells; counter++) {
grid[Math.floor(Math.random() * 9)][Math.floor(Math.random() * 9)] =
Math.floor(Math.random() * 9 + 1).toString();
}
return grid;
}
function createCorRandomGrid(numberOfRandomCells) {
var grid;
do {grid = createIncRandomGrid(numberOfRandomCells);}
while (checkGrid(grid) === false);
return grid;
}
function findPossibilitiesForCell(grid, row, column) {
var possibilities = [];
for (var value = 1; value < 10; value++) {
if (checkRowColumnBlock(grid, row, column, value)) {
possibilities.push(value.toString());
}
}
return possibilities;
}
function findPossibilitiesForGrid(grid) {
var result = [];
var listOfEmptyCells = findEmptyCells(grid);
var amountOfEmptyCells = listOfEmptyCells.length;
for (var cell = 0; cell < amountOfEmptyCells; cell++) {
var row = listOfEmptyCells[cell][0];
var column = listOfEmptyCells[cell][1];
result.push(findPossibilitiesForCell(grid, row, column));
}
return result;
}
function solveSudoku(grid) {
var emptyCell = findNextEmptyCell(grid);
if (emptyCell === false) {
return true;
}
var row = emptyCell.cell[0];
var column = emptyCell.cell[1];
var valueList = shuffle(emptyCell.possibilities);
var valueListLength = valueList.length;
for (var valueIndex = 0; valueIndex < valueListLength; valueIndex++) {
if (checkRowColumnBlock(grid, row, column, valueList[valueIndex])) {
grid[row][column] = valueList[valueIndex].toString();
if (solveSudoku(grid)) {
return grid;
}
grid[row][column] = "";
}
}
return false;
}
function countAllSolutions(grid) {
var nrOfSolutions = 1;
function solveAll(grid) {
var emptyCell = findNextEmptyCell(grid);
if (emptyCell === false || nrOfSolutions > 1) {
return true;
}
var row = emptyCell.cell[0];
var column = emptyCell.cell[1];
var valueList = shuffle(emptyCell.possibilities);
var valueListLength = valueList.length;
for (var valueIndex = 0; valueIndex < valueListLength; valueIndex++) {
if (checkRowColumnBlock(grid, row, column, valueList[valueIndex])) {
grid[row][column] = valueList[valueIndex].toString();
if (solveAll(grid)) {
nrOfSolutions++;
}
grid[row][column] = "";
}
}
return false;
}
solveAll(grid);
return nrOfSolutions-1;
}
function findPossibilitiesForFullCell(grid, row, column) {
var possibilities = [];
var originalValue = grid[row][column];
grid[row][column] = "";
for (var value = 1; value < 10; value++) {
if (checkRowColumnBlock(grid, row, column, value)) {
possibilities.push(value.toString());
}
}
grid[row][column] = originalValue;
return possibilities;
}
function findPossibilitiesForFullGrid(grid) {
var result = [];
var listOfFullCells = findFullCells(grid);
var amountOfFullCells = listOfFullCells.length;
for (var cell = 0; cell < amountOfFullCells; cell++) {
var row = listOfFullCells[cell][0];
var column = listOfFullCells[cell][1];
result.push(findPossibilitiesForFullCell(grid, row, column));
}
return result;
}
function sortFullCells(grid) {
var result = [];
var listOfFullCells = findFullCells(grid);
if (listOfFullCells === false) {
return false;
}
var listOfPossibilities = findPossibilitiesForFullGrid(grid);
var counter = listOfFullCells.length;
for (var cell = 0; cell < counter; cell++) {
result.push({"cell": listOfFullCells[cell], "possibilities": listOfPossibilities[cell]});
}
result.sort(function (first, second) {
return first.possibilities.length - second.possibilities.length;
});
return result;
}
function findNextFullCells(grid) {
var sortedFullCells = sortFullCells(grid);
if (sortedFullCells === false) {
return false;
}
var result = [];
result.push(sortedFullCells[0]);
for (var cell = 1, length = sortedFullCells.length; cell < length; cell++){
if(sortedFullCells[cell].possibilities.length === sortedFullCells[0].possibilities.length) {
result.push(sortedFullCells[cell]);
}
}
return result;
}
function removeCells(grid, cellsToBeRemoved) {
if (cellsToBeRemoved <= 0) {
return grid;
}
var nextCell = shuffle(findFullCells(grid))[0];
var row = nextCell[0];
var column = nextCell[1];
var value = grid[row][column];
grid[row][column] = "";
cellsToBeRemoved--;
if (countAllSolutions(grid) < 2) {
grid = removeCells(grid, cellsToBeRemoved);
return grid;
} else {
grid[row][column] = value;
grid = removeCells(grid, cellsToBeRemoved);
}
return grid;
}
createTable();
createButton("Solve Sudoku", function () {
console.time("Solved");
printGrid(solveSudoku(retrieveGrid()));
console.timeEnd("Solved");
});
createButton("Remove Cells", function () {
console.time("Removed");
printGrid(removeCells(retrieveGrid(),55));
console.timeEnd("Removed");
});
createButton("Count Solutions", function () {
console.time("Counting");
console.log(countAllSolutions(retrieveGrid()));
console.timeEnd("Counting");
});
createButton("Create Random Grid", function () {
printGrid(createIncRandomGrid(100));
});
createButton("Create Correct Random Grid", function () {
printGrid(createCorRandomGrid(17));
});
createButton("Check Grid", function () {
checkGrid(retrieveGrid());
});
createButton("Count Full Cells", function () {
console.log(findFullCells(retrieveGrid()).length);
});
createButton("Count Empty Cells", function () {
console.log(findEmptyCells(retrieveGrid()).length);
});
createButton("Sort Empty Cells", function () {
console.log(sortPossibilties(retrieveGrid()));
});
createButton("Sort Full Cells", function () {
console.log(sortFullCells(retrieveGrid()));
});
createButton("Reset Grid", function () {
printGrid(createEmptyGrid());
});
I haven't actually tested it but I did test a similar function.
Try this at the end, replacing your last eight lines:
if (countAllSolutions(grid) < 2) grid = removeNrs(grid, nrsToBeRemoved);
else grid[row][column] = value;
return grid;
I am working on this table that as to be managed by the client. I want to know if is possible to change the color of the entire row when in the "Status" column he writes the word "vermietet".
In this case when the client write "vermietet" the rows that contains that word change background color in orange.
Any JS tips?
Thanks in adivce.
EDIT:
I tried this
<script>
jQuery(document).ready(function($){
$(document.body)var cols = document.getElementsByClassName('column-10');
for (var i = 0; i < cols.length; ++i) {
var col = cols[i];
if (col.innerHTML === 'vermietet') {
var parent = col;
while((parent = parent.parentElement).tagName !== 'TR');
var found = parent.childNodes;
for (var j = 0; j < found.length; ++j) {
var td = found[j];
if (td.tagName === 'TD') {
td.style.backgroundColor = 'orange';
}
}
}
}
});
</script>
Pure JS solution:
var cols = document.getElementsByClassName('column-10');
for (var i = 0; i < cols.length; ++i) {
var col = cols[i];
if (col.innerHTML === 'vermietet') {
var parent = col;
while((parent = parent.parentElement).tagName !== 'TR');
var found = parent.childNodes;
for (var j = 0; j < found.length; ++j) {
var td = found[j];
if (td.tagName === 'TD') {
td.style.backgroundColor = 'orange';
}
}
}
}
I have a table in my popup and I save the values entered by a user to localStorage. Here are the snippets.
popup.html
<table id="main_table">
</table>
<script src="popup.js"></script>
popup.js
function create_row() {
localStorage["last_session"] = true;
var table = document.getElementById("main_table");
var n = table.rows.length;
var m = table.rows[0].cells.length;
var row = table.insertRow(n);
if (!localStorage['use_storage']) {
if (n === 1) {
localStorage["cells"] = JSON.stringify([{}]);
}
else if (n > 1) {
var cells = JSON.parse(localStorage["cells"]);
cells.push({});
localStorage["cells"] = JSON.stringify(cells);
}
}
var cell = row.insertCell(0);
cell.innerHTML = n;
for (j=1; j<m; j++) {
create_cell(n-1, j, row);
}
return row
}
function create_cell(i, j, row){
var cell = row.insertCell(j);
if (j == 1) {
cell.innerHTML = "<input size=10>";
}
else {
cell.innerHTML = "<input size=4>";
}
cell.addEventListener("change", function () {
var cells = JSON.parse(localStorage["cells"]);
cells[i.toString()][j.toString()] = cell.childNodes[0].value;
localStorage["cells"] = JSON.stringify(cells);
})
}
document.getElementById('create_row').onclick = create_row;
// restore a table
if (localStorage["last_session"]) {
localStorage["use_storage"] = true;
try {
var cells = JSON.parse(localStorage["cells"]);
var n = cells.length;
var table = document.getElementById("main_table")
for (i=0; i<n; i++) {
var row = create_row(true);
var cell = cells[i]
for (var key in cell) {
if (cell.hasOwnProperty(key)) {
var col = parseInt(key);
var val = cell[key];
row.cells[col].childNodes[0].value = val;
}
}
}
} catch (e) {
console.log("Catched error");
console.log(e);
}
if (localStorage["results"]) {
show_results();
}
localStorage['use_storage'] = false;
}
In my browser it works as it is supposed, that is after refreshing a page popup.html I have the state where I left (number of rows and values are preserved). However, in my chrome extension, after clicking to any area and thus reloading the extension, I have the initial empty table.
How can I preserve the table in this particular case?
I have the following jquery method:
$('.niGridTable table tr').addClass('selected').end().click(function (event) {
event = event || window.event;
var isClassExist = false;
var closesedTable = $(event.target).closest('tr').find('.selected_row');
if (closesedTable.length > 0) {
isClassExist = true;
if (event.ctrlKey) {
for (var i = 0; i < closesedTable.length; i++) {
if ($(closesedTable[i]).hasClass('selected_row')) {
$(closesedTable[i]).removeClass('selected_row');
}
}
}
}
if (!event.ctrlKey) {
if ($('td').hasClass('selected_row')) {
$('td').removeClass('selected_row');
}
}
if (!isClassExist) {
$('.table-striped > tbody > tr:hover > td').addClass('selected_row');
}
});
I want to write such code as angular way.like...
element.on('click', function (event) {
}
In my directive I have changed my question said jquery to Angularjs. In this case I have use angular.element.
$timeout(function () {
//get all row for set selected row class
var trs = iElement.find('tr');
for (var index = 0; index < trs.length; index++) {
var tableTr = angular.element(trs[index]);
//remove prvious click event
tableTr.unbind('click');
tableTr.bind('click', function (event) {
event = event || window.event;
event.stopPropagation();
event.preventDefault();
//if target row contain previos selection then remove such selection
var isClassExist = false;
var targetTd = angular.element(event.target);
var targetTr = targetTd.parent();
if (targetTr.hasClass('selected_row')) {
targetTr.removeClass('selected_row');
isClassExist = true;
}
//if another row contain selection then remove their selection but if control button pressed then it will not work
var closesedTable = iElement.find('tr');
if (closesedTable.length > 0) {
if (!event.ctrlKey) {
for (var i = 0; i < closesedTable.length; i++) {
var eachRow = angular.element(closesedTable[i]);
if (eachRow.hasClass('selected_row')) {
eachRow.removeClass('selected_row');
}
}
}
}
//set selection
if (!isClassExist) {
targetTr.addClass('selected_row');
}
////get selected rows
//for (var j = 0; j < trs.length; j++) {
// gridOption['selectedRow'].push();
//}
});
}
}, 0);
I'm learning phonegap by making a simple t-do app.
index.html:
<!DOCTYPE html>
<html>
<head>
<title>To-dos</title>
</head>
<body onload='loadToDoList()'>
<input type='button' value='Add To-do' onclick='createNewToDo()'/>
<input type='button' value='Remove completed To-dos' onclick='removeCompletedTasks()'/>
<br/><br/>
<table id='dataTable' width='100%' border='1'>
</table>
<script src='js/index.js'></script>
</body>
</html>
index.js:
(function() {
var addTableRow, checkboxClicked, deleteAllRows, deleteSelectedRow, loadToDoList, saveToDoList, viewSelectedRow;
window.rowID = 0;
saveToDoList = function() {
var checkBoxState, chkbox, i, row, rows, table, textValue, textbox, todoArray, _i, _len;
todoArray = {};
checkBoxState = 0;
textValue = '';
table = document.getElementById('dataTable');
rows = table.rows;
if (rows.length > 0) {
i = 0;
for (_i = 0, _len = rows.length; _i < _len; _i++) {
row = rows[_i];
chkbox = rows.cells[0].childNodes[0];
if ((chkbox != null) && chkbox.checked === true) {
checkBoxState = 1;
} else {
checkBoxState = 0;
}
textbox = row.cells[1].childNodes[0];
textValue = textbox.value;
todoArray["row" + (i++)] = {
check: checkBoxState,
text: textValue
};
}
} else {
todoArray = null;
}
return window.localStorage.setItem('todoList', JSON.stringify(todoArray));
};
loadToDoList = function() {
var i, key, theList, val, _results;
theList = JSON.parse(window.localStorage.getItem('todoList'));
deleteAllRows();
if (theList !== null && theList !== 'null') {
i = 0;
_results = [];
for (key in theList) {
val = theList[key];
_results.push(addTableRow(theList["row" + (i++)], true));
}
return _results;
}
};
deleteAllRows = function() {
var i, rowCount, table, _i;
table = document.getElementById('dataTable');
rowCount = table.rows.count;
for (i = _i = 0; 0 <= rowCount ? _i < rowCount : _i > rowCount; i = 0 <= rowCount ? ++_i : --_i) {
table.deleteRow(i);
rowCount--;
i--;
}
return saveToDoList();
};
viewSelectedRow = function(todoTextField) {
return alert(todoTextField.value);
};
deleteSelectedRow = function(deleteButton) {
deleteButton.parentNode.parentNode.parentNode.removeChild();
return saveToDoList();
};
checkboxClicked = function() {
var chkbox, row, rows, table, textbox, _i, _len, _results;
table = document.getElementById('dataTable');
rows = table.rows;
_results = [];
for (_i = 0, _len = rows.length; _i < _len; _i++) {
row = rows[_i];
chkbox = row.cells[0].childNodes[0];
textbox = row.cells[1].childNodes[0];
if ((chkbox != null) && chkbox.checked === true && (textbox != null)) {
textbox.style.setProperty('text-decoration', 'line-through');
} else {
textbox.style.setProperty('text-decoration', 'none');
}
_results.push(saveToDoList());
}
return _results;
};
addTableRow = function(todoDictionary, appIsLoading) {
var cell1, cell2, element1, element2, row, rowCount, table;
rowID++;
table = document.getElementById('dataTable');
rowCount = table.rows.length;
row = table.insertRow(rowCount);
cell1 = row.insertCell(0);
element1 = document.createElement('input');
element1.type = 'checkbox';
element1.name = 'chkbox[]';
element1.checked = todoDictionary['check'];
element1.setAttribute('onclick', 'checkboxClicked()');
cell1.appendChild(element1);
cell2 = row.insertCell(1);
element2 = document.createElement('input');
element2.type = 'text';
element2.name = 'txtbox[]';
element2.size = 16;
element2.id = 'text' + rowID;
element2.value = todoDictionary['text'];
element2.setAttribute('onchange', 'saveToDoList()');
cell2.appendChild(element2);
checkboxClicked();
saveToDoList();
if (!appIsLoading) {
return alert('Task added successfully');
}
};
window.createNewToDo = function() {
var todo, todoDictionary;
todoDictionary = {};
todo = prompt('To-Do', '');
if (todo != null) {
if (todo === '') {
return alert('To-Do can\'t be empty');
} else {
todoDictionary = {
check: 0,
text: todo
};
return addTableRow(todoDictionary, false);
}
}
};
window.removeCompletedTasks = function() {
var chkbox, i, rowCount, table, _i;
table = document.getElementById('dataTable');
rowCount = table.rows.length;
for (i = _i = 0; 0 <= rows ? _i < rows : _i > rows; i = 0 <= rows ? ++_i : --_i) {
chkbox = table.rows[i].cells[0].childNodes[0];
if ((chkbox != null) && chkbox.checked === true) {
table.deleteRow(i);
i--;
rowCount--;
}
}
saveToDoList();
return alert('Completed tasks were removed');
};
}).call(this);
My createNewToDo function works fine, since I'm able to create new todos. But when I check a todo and click on the remove button, nothing happens. Why is my removeCompletedTasks function not being called?
Here is the provided code in JSFiddle. The primary reason that I can see for removeCompletedTasks failing is that rows is undefined. After that is fixed, the other errors are caused by the methods you have defined only being in scope inside of the outside function. Once it completes, they are gone and cannot be called.
Each of the methods need to have window. before them. Then the saveToDoList needs a little work. the table method doesn't work, just go directly for the rows. Once you have the row you want use that reference.
window.saveToDoList = function() {
var checkBoxState, chkbox, i, row, rows, textValue, textbox, todoArray, _i, _len;
todoArray = {};
checkBoxState = 0;
textValue = '';
rows = document.getElementById('dataTable').rows;
if (rows.length > 0) {
i = 0;
for (_i = 0, _len = rows.length; _i < _len; _i++) {
row = rows[_i];
chkbox = row.childNodes[0];
if ((chkbox != null) && chkbox.checked === true) {
checkBoxState = 1;
} else {
checkBoxState = 0;
}
textbox = row.childNodes[1];
textValue = textbox.value;
todoArray["row" + (i++)] = {
check: checkBoxState,
text: textValue
};
}
} else {
todoArray = null;
}
return window.localStorage.setItem('todoList', JSON.stringify(todoArray));
};
With that method working, now the removeCompletedTasks needs some fine tuning. The loop can be simplified to a simple for loop. Removing the row is done by getting it fresh each time, I know this is slower, but at least chrome returns a function for table the way it was. To get the checkbox, one more level of children needed peeling, and now it works.
window.removeCompletedTasks = function() {
var chkbox, i, rowCount, rows;
rows = document.getElementById('dataTable').rows;
rowCount = rows.length;
for (i = 0; i < rowCount; ++i) {
chkbox = rows[i].childNodes[0].childNodes[0];
if ((chkbox != null) && chkbox.checked === true) {
document.getElementById("dataTable").deleteRow(i);
--i;
--rowCount;
}
}
saveToDoList();
return alert('Completed tasks were removed');
};
Check out the new JSFiddle for the working example.