LocalStorage- not able to remove items from todo list - javascript

I've recently started to work my Todo list without relying on coding tutorials and it's so difficult. I'm able to add items in my list, but I'm not able to remove items. When I click on items, all items gets deleted. Could you help please?
<fieldset>
<legend class="task-list-title">Goals that I need to work on</legend>
<input
id="inpKey"
type="text"
placeholder="add new goal"
aria-label="new list name"
<button type="button" id="btnInsert">New Goal</button>
<div id="task_list">
</div>
</fieldset>
JavaScript Code
const inpKey = document.getElementById("inpKey");
const btnInsert = document.getElementById("btnInsert");
const task_list = document.getElementById("task_list");
btnInsert.onclick = function() {
const key = inpKey.value;
if(key) {
localStorage.setItem(key, inpKey.value);
location.reload();
}
}
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
task_list.innerHTML += `${key}<br />`
}
task_list.onclick = function() {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
window.localStorage.removeItem(key);
task_list.innerHTML = key;
}
}

So I believe the problem was you not checking for which particular todo to delete, instead you were deleting them all
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
// I it would be better if you wrapped each key in its own element
task_list.innerHTML += `<div>${key}</div>`; // I'm using a `div` so there'll be no need for a `<br/>` tag
}
task_list.onclick = function(e) {
// I want to use the event data to find out exactly which localStorage key to delete
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
// Here i'm checking if the key is the same as the todo that you want to delete
if (key == e.target.innerText) {
localStorage.removeItem(key); // Removing the todo from storage
e.target.remove(); // Removing the todo element from the DOM
}
}
}

Appending the new key to the innerHTML for tasks_list makes it very difficult to get the key when you want to delete the item. Instead it would be easier to make each new task a <p> element (or an unordered list element) and to append that new element as a child to tasks_list like so-
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const newTask = document.createElement('p') //create a new html element
newTask.classList.add('task') //add a class of "task" to it
newTask.innerHTML = key //set the innerHTML to equal the key
task_list.appendChild(newTask) //add the newTask element as a child element of task_list
}
You can add event listeners to each element with the task class by looping through each and adding your event listener. The nice thing here is that when you want the key for an individual task you need only grab the innerHTML and use that-
for(let i = 0; i < tasks.length; i++) {
tasks[i].addEventListener("click", () => {
const key = tasks[i].innerHTML
window.localStorage.removeItem(key)
location.reload()
})
}
The way you currently removed items by looping through localStorage was actually removing an element on each pass through of the loop and not checking to see if the item it was removing was the item that had been clicked.
EDIT- a. mola's answer does something similar to mine but you don't need to loop through the entirety of local storage to find your matching key. That's the beauty of having key/value pairs! As long as you know the key you can manipulate the data without having to first search for it through a loop.

Related

Push all objects after a selected object into an array

I have a web page that returns a list of objects like:
date.pdf
names.csv
address.pdf
age.csv
cost.csv
budget.csv
data.pdf
race.pdf
contractors.csv
When a user checks budget.csv, I want every object with the .csv extension from that point to be pushed into csv_files[]. If they select names.csv, then every .csv including and after names is pushed into the array.
So the only data that gets pushed into the array is from the selected object downwards. How can I implement this?
Current code
const csv_files = []
$scope.listAllobjects = (err, data) => {
$.each(data.Contents, (index, value) => {
if (value.Key.endsWith("csv")) {
csv_files = [];
}
// Handle click on selection checkbox
$("#filesobjects-table tbody").on("click", 'input[type="checkbox"]', (e1) => {
const checkbox = e1.currentTarget;
const $row = $(checkbox).closest("tr");
const data = $tb.DataTable().row($row).data();
let index = -1;
// Prevent click event from propagating to parent
e1.stopPropagation();
// Find matching key in currently checked rows
index = $scope.view.keys_selected.findIndex((e2) => e2.Key === data.Key);
if (checkbox.checked && data.Key.endsWith("csv")) {
console.log(selected csv)
}
});
}
There's a few ways, I suppose, to approach this problem, but the most intuitive to me is this:
const csvList = ["date.pdf","names.csv","address.pdf","age.csv","cost.csv","budget.csv","data.pdf","race.pdf","contractors.csv"];
const selectedCsv = 'budget.csv';
function getCsvsAfter(csvList, selectedCsv) {
const filteredCsvs = [];
let found = false;
for (let csv of csvList) {
if (csv === selectedCsv) found = true;
if (found) filteredCsvs.push(csv);
}
return filteredCsvs;
}
console.log(getCsvsAfter(csvList, selectedCsv));
Iterate over every csv, and when you've hit the one you're trying to match, set a variable called found to true. Once it's true, you can add every following csv onto the list.
const list = ['date.pdf','names.csv','address.pdf','age.csv','cost.csv','budget.csv','data.pdf','race.pdf','contractors.csv'];
const selected = 'budget.csv'
const csv_files = list.slice(list.indexOf(selected))
console.log(csv_files)
Here you go with a pure JavaScript solution (Descriptive comments has been added in the below code snippet).
var contentData = ["date.pdf", "names.csv", "address.pdf", "age.csv", "cost.csv", "budget.csv", "data.pdf", "race.pdf", "contractors.csv"];
var myDiv = document.getElementById("cboxes");
for (var i = 0; i < contentData.length; i++) {
var checkBox = document.createElement("input");
var label = document.createElement("label");
checkBox.type = "checkbox";
checkBox.value = contentData[i];
myDiv.appendChild(checkBox);
myDiv.appendChild(label);
label.appendChild(document.createTextNode(contentData[i]));
}
// Event to handle the checkbox click
document.getElementById('getResult').addEventListener('click', () => {
document.getElementById('showResult').innerHTML = getCheckedValues();
});
function getCheckedValues() {
// filtered out the checked items.
const element = Array.from(document.querySelectorAll('input[type="checkbox"]'))
.filter((checkbox) => checkbox.checked).map((checkbox) => checkbox.value);
// element[0] will always return the first checked element and then we are getting index of that.
const checkedElemIndex = contentData.indexOf(element[0]);
// Slice the content data to get the elements from the checked element index.
return contentData.slice(checkedElemIndex, contentData.length)
}
<div id="cboxes"></div>
<button id="getResult">Get Result</button>
<pre id="showResult"></pre>

JavaScript append multiple in for loop

Does anyone know what I'm doing wrong with the append?
function filterPosts(){
let filterValue = document.getElementById('search-filter').value.toUpperCase();
let posts = document.getElementById('posts');
let post = posts.querySelectorAll('div.post');
for (let i = 0; i < post.length; i++) {
let filterItem = post[i].getElementsByTagName('h5')[0];
if (filterItem.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
post[i].append();
} else {
post[i].remove();
}
}
}
I've tried a few different things to no avail. I'm trying to remove elements based on type and then readd them if they exist based on heading.
for (let i = 0; i < post.length; i++) {
let filterItem = post[i].getElementsByTagName('h5')[0];
if (filterItem.innerHTML.toUpperCase().indexOf(filterValue) > -1) {
posts.appendChild(post[i]);
} else {
post[i].remove();
}
}
This ended up working for the filtering, but it doesnt bring back posts if textbox is backspaced or empty.
you could use https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild (has better browser support than append) and https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild - in your case this would looks like posts.appendChild(post[i]) and posts.removeChild(post[i])
you could also just change the visibility of the elements - this should be faster and simpler for your case.
another way would be to have an array with the all the posts data and work with it primarily. this way you can also "bring back the data".
each time the filter changes: create a new array by filtering the posts data, remove all post elements, then append only those that match the filtered data e.g.
let postsData = ["content1", "content2"];
for (let i = 0; i < post.length; i++) {
post[i].remove();
}
postsData.filter(function (post) {
return post.toUpperCase().indexOf(filterValue) > -1;
}).forEach(function () {
var postElem = document.createElement("div");
posts.appendChild(postElem);
});

Javascript: Find the Parent Element Text

How do I find the Parent Attribute for a data-element (data-qa). I have a data attribute id, data-qa=productId1 , productid2, productid3. (for each row in the grid)
For each row number in a grid.
How do I tell if the parent Element has kendogridcell above?
I don't know what kendogridcell is, its not a class.
let dataQaElements = document.querySelectorAll(`[data-qa]`);
for (let i = 0; i < dataQaElements.length; i++) {
let attr = dataQaElements[i].parentElement;
// not sure what next code is
}
Currently team is doing Selenium test automation.
const dataQaElements = document.querySelectorAll(`[data-qa]`);
for (let el of dataQaElements) {
const parent = el.parentElement;
const kendogridcell = parent.getAttribute("kendogridcell"); // if exists "" or null if does not exist
}

Target all the specific elements except the on clicked on

Inside the loop in the function thirdNavFunction, I need to target all the .third-nav classes except the one clicked on and it needs to be done without jQuery, how do I do this?
var thirdNav = document.getElementsByClassName("third-nav");
for (var i = 0; i < thirdNav.length; i++){
thirdNav[i].addEventListener("click", thirdNavFuntion);
}
function thirdNavFuntion() {
for (i = 0; i < thirdNav.length; i++) {
thirdNav[i].parentElement.className = "";
}
if (this.parentElement.className === "") {
this.parentElement.className = "nav-sub-li-active";
} else {
this.parentElement.className = "";
}
}
In the code here, I target all the .third-nav divs, but that doesn't work as intended, I need to exclude the one that is clicked. I hope it makes sense.
Convert thirdNav to array using Array.from().
Filter out the clicked element using the filter() method.
Call the forEach method to change the class name of all parent elements.
function thirdNavFuntion() {
const elements = Array.from(thirdNav).filter(el => el !== this)
elements.forEach(el => el.parentElement.className = "")
}

obtaining all items of listView in winjs

I have a listview div on screen and I have added itemDataSource to it successfully
var lettersList = new WinJS.Binding.List(jsonArrayForClearance);
var list_ = document.getElementById("prodListView").winControl;
list_.itemDataSource = lettersList.dataSource;
list_.itemTemplate = document.getElementById("tileTemplate");
list_.forceLayout();
Now in each item I have added a custom input type for user to specify(using template). I want to iterate through all the items of list and obtain the value of that input type in an array.
how can I do it?
EDIT: My question is to access custom input type declared in list items. As such we use following code to access any input type named "inpT"
document.getElementById('inpT');
but how to access the same from list item? can I use Following code(as suggested by user2608614)
var listView = document.getElementById("prodListView").winControl;
var list = listView.itemDataSource.list;
for (var i = 0; i < list.length; i++) {
var item = list.getAt(i);
item.getElementById('inpT');
}
You can iterate through the list elements like this:
var listView = document.getElementById("prodListView").winControl;
listView.itemDataSource.getCount()
.done(function(count) {
for (var i = 0; i < count ; i++) {
listView.itemDataSource.itemFromIndex(i)
.done(function (item) {
//***item will contain your property
});
}
});
Is not the best solution as it make it difficult to chain the promises, I'm also looking for a better one. But it works.
Since you're using a Binding.List you can just iterate through that much like an array.
var listView = document.getElementById("prodListView").winControl;
var list = listView.itemDataSource.list;
for (var i = 0; i < list.length; i++) {
var item = list.getAt(i);
// do something with this item
}
The only thing to remember is that it doesn't support [] and instead you have to use .getAt() and .setAt().

Categories