I am trying to add another button that will remove tasks from the list, and allow the user to remove any of them. I am trying to use splice with indexOf but it's not working so far. Here is the code. Thanks for the help.
// tasks.js #2
// This script manages a to-do list.
// Need a global variable:
var tasks = [];
// Function called when the form is submitted.
// Function adds a task to the global array.
function addTask() {
'use strict';
// Get the task:
var task = document.getElementById('task');
// Reference to where the output goes:
var output = document.getElementById('output');
// For the output:
var message = '';
if (task.value) {
// Add the item to the array:
tasks.push(task.value);
// Update the page:
message = '<h2>To-Do</h2><ol>';
for (var i = 0, count = tasks.length; i < count; i++) {
message += '<li>' + tasks[i] + '</li>';
}
message += '</ol>';
output.innerHTML = message;
} // End of task.value IF.
// Return false to prevent submission:
return false;
} // End of addTask() function.
function deleteTask() {
var inputTask = document.getElementById('task');
var taskLength = inputTask.length;
var i = array.indexOf("inputTask");
if (i != -1) {
array.splice(i, taskLength);
}
}
// Initial setup:
function init() {
'use strict';
//document.getElementById('theForm').onsubmit = addTask;
var elem1 = document.getElementById("submit");
elem1.addEventListener("click", addTask, false);
var elem2 = document.getElementById("delete");
elem2.addEventListener("click", deleteTask, false);
} // End of init() function.
window.onload = init;
You store a reference to an element #inputTask in the inputTask variable but then try to get the index of a string "inputTask" in an array array (which does not exsist, as mentionned by #Frits in the comments).
Then you try to splice the array with the index and the length of inputTask which has no length because it's an element, and if it was a string, why use its length to splice ?
Splice removes (and adds) elements : the first argument is the index of the first element you want to remove, the second agrument is the number of elements you want to remove from the array. So if you want to remove one element, it should look like array.splice(index, 1)
If you want to build deleteTask function the same way as addTask built, you need to implement the following algorithm:
1) find the task element in DOM and get its value
2) check whether or not the value is in the `tasks` array
3) if it's there, remove it
Here's one approach to do this:
function deleteTask() {
// 1
var taskEl = document.getElementById('task');
// 2
var taskIndex = tasks.indexOf(taskEl.value);
if (taskIndex !== -1) {
// 3
tasks.splice(taskIndex, 1);
}
return false;
}
In practice, though, I'd probably go a bit different way. Currently addTask and deleteTask have the same code for collecting the value of this task, but it's preventable - just extract the code that retrieves the value into a separate action (named something like getCurrentTask) and make these methods work with task param instead.
const array = ["a", "b", "c", "d"];
const index = array.indexOf("c");
if (index > -1) {
array.splice(index, 1);
}
console.log(array); // ["a","b","d"]
Related
I've been stuck trying to figure out how to retrieve a desired index. I have a user adding names to a Guestlist, with the option to remove names. All the names are appended to an array saved to localStorage. When the delete button is clicked, I want to iterate over the array, find the matching string and retrieve it's index. All I'm getting however, is a -1 if the string doesn't match, and 0 if it matches. Where is my error? Any help is appreciated.
deleteBtn.addEventListener('click', function () { // Makes the Delete button active.
const deleteName = this.parentElement.innerText; // parent element is li
const test = localStorage.getItem('data');
const testParsed = JSON.parse(test);
for (let i = 0; i < testParsed.length; i++) {
let compare = `Name: ${testParsed[i].name}, About: ${testParsed[i].about}Delete entry`; // matches innerHtml of deleteName
compare.replace('.,:', ''); // Removes unwanted punctuation
console.log('Compare: ', compare);
function index(){
return deleteName === compare;
}
console.log(testParsed.findIndex(index));
I played around some more, and figured it out. I'll leave the question up in case someone else can make use of it.
deleteBtn.addEventListener('click', function () { // Makes the Delete button active.
const deleteName = this.parentElement.innerText; // parent element is li
const test = localStorage.getItem('data');
const testParsed = JSON.parse(test);
console.log(test);
for (let i = 0; i < testParsed.length; i++) {
let compare = `Name: ${testParsed[i].name}, About: ${testParsed[i].about}Delete entry`; // matches innerHtml of deleteName
compare.replace('.,:', ''); // Removes unwanted punctuation
console.log('Compare: ', compare);
if(deleteName === compare) {
function index(equals) { // compare is string, equals is object
return equals.name === testParsed[i].name && equals.about === testParsed[i].about;
}
console.log(testParsed.findIndex(index));
}
I'm having trouble solving this question (the result is always undefined) and I am not sure what I'm doing wrong... any ideas?
Write a function that takes a number and generates a list from 0 to that number.
Use the function to assign a value to the myNumberList variable so that it has the value of a list going from 0 to 5.
Assign a value to the variable secondLastItem that should be the second last item of the myNumberList array.
function listMaker(listLength) {}
var myNumberList = null; // replace with number list created by listmaker
var secondLastItem = null; // replace with second last item
You can try the following way using ES6's spread operator (...):
function listMaker(listLength) {
return [...Array(listLength).keys()];
}
var myNumberList = listMaker(10);
// If you want the specified number passed as argument to be included as the last item in the array then push it.
myNumberList.push(10);
console.log(myNumberList);
Here is one way to write it:
function listMaker(number) {
var secondToLast;
var list = [];
for (var i = 0; i <= number; i++){
list.push(i);
}
secondToLast = list[list.length - 2];
return [list, secondToLast]
}
var list = listMaker(5)[0];
var secondToLast = listMaker(5)[1]
console.log(list + "\n" + secondToLast);
That is the snippet ^
Here is the jsfiddle
Intro
I have a search bar I implemented into my website which searches through member cards to find matching cards. I also used Twitter's typeahead.js for this. The results are updated as you type, so I set an event listener on the input box - $('#members-search .typeahead').on("input", changeFunction); I also needed to set a click event listener on the suggestions, as I did - $('.tt-suggestion').on("click", changeFunction);
Problem
It seems like the suggestion boxes are created on the fly, so you can't set an event listener for all (or even any!) of them at the beginning. My first idea was to fire a function when an element was appended in the containing div. However, you would need an event listener for that, and I couldn't find one. Is there any way to implement this?
Code
The JavaScript:
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var children = document.getElementById("members-list").children;
var names = [];
var whoIsWho = [];
var selected = [];
var listOfAttributeNames = ["data-member-name", "data-member-username", "data-member-nickname"];
for(var i = 0; i < children.length; i++){
for(var j = 0; j < listOfAttributeNames.length; j++){
var a;
if(a = children[i].getAttribute(listOfAttributeNames[j])){
names.push(a);
whoIsWho.push(children[i]);
}
}
}
$('#members-search .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'names',
source: substringMatcher(names)
});
var previousValue = "";
function changeFunction(e){
var v;
if($("#members-search .typeahead").val() === ""){
previousValue = "";
}
else if(((v = $('#members-search .typeahead+pre').text())) !== previousValue){
previousValue = v;
}
else if(v !== $("#members-search .typeahead").val()){
previousValue = $("#members-search .typeahead").val();
}
selected = [];
v = $('#members-search .typeahead+pre').text();
for(var i = 0; i < names.length; i++){;
if(!(new RegExp(v, "i").test(names[i])) && !(selected.includes(whoIsWho[i]))){
whoIsWho[i].style.display = "none";
}
else{
selected.push(whoIsWho[i]);
whoIsWho[i].style.display = "block";
}
}
}
$('#members-search .typeahead').on("input", changeFunction);
$('.tt-suggestion').on("click", changeFunction);
The (important) HTML:
<div id="members-search">
<input class="typeahead" type="text" placeholder="Search">
</div>
Alternate, Backup Solutions
I could copy the bloodhound script over to my code and modify where the elements are appended, but I'd rather not, as it uses this weird format of IIFE that I won't take the time to understand. Or is there another solution and this question is part of the X/Y problem?
It turns out I had the wrong approach. I just added an event listener to the current suggestions every time the input value was changed.
$('#members-search .typeahead').on("input", function(){
$('.tt-suggestion').on("click", changeFunction);
});
Here is the scenario:
There is a parameter titledlistOfSelectedProductIdsthat contains
all of the selected ids.
There is another list titled listOfAllPossibleProducts, which
contains a list of objects. That object contains a ProductId,
ProductName, and ProductCode. It looks something like this:
The task at hand:
I need to loop through my listOfSelectedProductIds. If the ProductId matches a ProductId from listOfAllPossibleProducts, then I need to return that object.
Here is what I am doing:
function SelectedProducts(listOfSelectedProductIds){
for (var index = 0; index < listOfSelectedProductIds.length; index++) {
var currentItem = listOfSelectedProductIds[index];
var desiredProduct = _.contains(listOfAllPossibleProducts, currentItem);
if (desiredProduct === true) {
return listOfAllPossibleProducts[index];
}
}
}
What's currently happening:
My loop is getting the selected id as expected i.e. currentItem, but _.contains(...)
always returns false.
Question:
What is the best way to find the objects in
listOfAllPossibleProducts that have ProductIds that match my
ProductIds in the listOfSelectedProductIds
How about using _.filter:
var result = _.filter(listOfAllPossibleProducts, function (el) {
return _.contains(listOfSelectedProductIds, el.id);
});
Or the non-underscore method:
var result = listOfAllPossibleProducts.filter(function (el) {
return listOfSelectedProductIds.indexOf(el.id) > -1;
});
DEMO
create another structure productsByProductId once!
var productsByProductId = {};
listOfAllPossibleProducts.forEach(p => {
productsByProductId[p.ProductId()] = p
});
and maybe a helper function
function getProductById(id){
return productsByProductId[id];
}
and use this to map the ids to the nodes
var selectedProducts = listOfSelectedProductIds.map(getProductById)
https://stackoverflow.com/a/1234337/1690081
shows that array.length = 0;will empty array but in my code it doesn't
here's an sample:
window.onload = draw;
window.onload = getFiles;
var namelist = [];
function draw(){
// assing our canvas to element to a variable
var canvas = document.getElementById("canvas1");
// create html5 context object to enable draw methods
var ctx = canvas.getContext('2d');
var x = 10; // picture start cordinate
var y = 10; // -------||---------
var buffer = 10; // space between pictures
for (i=0; i<namelist.length; i++){
console.log(namelist[i])
var image = document.createElement('img');
image.src = namelist[i];
canvas.appendChild(image);
ctx.drawImage(image,x,y,50,50);
x+=50+buffer;
}
}
function getFiles(){
namelist.length = 0;// empty name list
var picturesFiles = document.getElementById('pictures')
picturesFiles.addEventListener('change', function(event){
var files = picturesFiles.files;
for (i=0; i< files.length; i++){
namelist.push(files[i].name);
console.log(namelist)
}
draw();
}, false);
}
after i call getFiles() second time. It doesn't remove the previous list, just appends to it. any idea why?
You should empty the array in the event handler, not getFiles which is only called once per pageload. It is actually doing nothing because the array is already empty when the page loads.
picturesFiles.addEventListener('change', function(event){
namelist.length = 0; //empty it here
var files = picturesFiles.files;
for (i=0; i< files.length; i++){
namelist.push(files[i].name);
console.log(namelist)
}
draw();
}, false);
Another problem is that you cannot just set .src to the name of a file. That would make the request to your server for the file.
To really fix this, just push the file objects to the namelist:
namelist.push(files[i]);
Then as you process them in draw, create localized BLOB urls to show them:
var file = namelist[i];
var url = (window.URL || window.webkitURL).createObjectURL( file );
image.src = url;
It looks like you're using namelist as a global variable. This would be easier (and would avoid needing to empty it at all) if you passed the new array out of the function as a return value.
ie:
function getFiles() {
var newNameList = [];
..... //push entries here.
return newNameList;
}
... and then populate namelist from the return value where you call it:
namelist = getFiles();
However, to answer the question that was actually asked:
Instead of setting the length to zero, you can also reset an array simply by setting it to a new array:
namelist = [];
You haven't shown us how you're 'pushing' entries to the list, but I suspect that the end result is that namelist is being generated as a generic object rather than an array object. If this is the case, then setting .length=0 will simply add a property to the object named length with a value of 0. The length property in the way you're using it only applies to Array objects.
Hope that helps.
If you are using non-numeric indexes then the array will not clear.
"...whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted"
Test:
var arr = [];
arr['this'] = 'that';
arr.length = 0;
console.log(arr);
//output ['this':'that']
var arr = [];
arr[0] = 'that';
arr.length = 0;
console.log(arr);
//output []
There is nothing wrong with how you empty the array, so there has to be something else that is wrong with your code.
This works fine, the array doesn't contain the previous items the second time:
var namelist = [];
function draw() {
alert(namelist.join(', '));
}
function getFiles() {
namelist.length = 0; // empty name list
namelist.push('asdf');
namelist.push('qwerty');
namelist.push('123');
draw();
}
getFiles();
getFiles();
Demo: http://jsfiddle.net/Guffa/76RuX/
Edit:
Seeing your actual code, the problem comes from the use of a callback method to populate the array. Every time that you call the function, you will add another event handler, so after you have called the function the seccond time, it will call two event handlers that will each add all the items to the array.
Only add the event handler once.