I have an object with some information. On the other hand I have a store, holding many objects of this object type.
The single object should be able to delete itself from this store.
My store knows a function "DeleteObjectFromStore" with a parameter of this object.
Now I want to pass in the object itself but I don't know which syntax to use.
The store:
class NoteStore {
constructor() {
this.notes = []; // Collection of all notes
}
DeleteNote(note) { // Delete this object from the store
var index = this.notes.indexOf(note);
if (index > -1)
array.splice(index, 1);
}
}
And my object
class Note {
constructor(noteTitle, noteText, noteStore) {
this.title = noteTitle; // name
this.text = noteText; // content
this.store = noteStore; // dataStore
}
CreateDeleteButton(parentDiv) { // Create a button for deleting itself
var data = this.store;
var deleteBtn = document.createElement("input");
deleteBtn.type = "button";
deleteBtn.value = "Delete";
deleteBtn.onclick = function() {
data.DeleteNote(); // <= missing parameter!
}
parentDiv.appendChild(deleteBtn);
}
}
So using the keyword this is not correct. Then I would pass in the button.
Could someone help me out?
Try using a name for this outside the function:
CreateDeleteButton(parentDiv) { // Create a button for deleting itself
var data = this.store;
var noteObj = this;
var deleteBtn = document.createElement("input");
deleteBtn.type = "button";
deleteBtn.value = "Delete";
deleteBtn.onclick = function() {
data.DeleteNote( noteObj );
}
parentDiv.appendChild(deleteBtn);
}
Related
Im creating an oop project with multiple functions in a class. I have a piece of code, but since I'm using bind it doesn't work anymore.
document.getElementById("finalLink").innerHTML +=
"<a id='FLink' href='https://www.voetbalshop.nl/voetbalschoenen.html#' onclick='location.href=this.href+getLink(url);return false;'>Result</a>";
class QuestionControl{
/**
* #param {QuizPart[]} quiz
*/
constructor(quiz) {
this.quiz = quiz;
this.url = [];
this.questionNumber = -1;
this.button = document.getElementById('answer');
this.questionName = document.getElementById('question');
this.nextbtn = document.getElementById('nextbtn');
this.prevbtn = document.getElementById('prevbtn')
this.resultbtn = document.getElementById('FLink');
}
Initialize(){
this.NextQuestion();
}
/**
*
* #param {int} question
* #returns {QuizPart}
*/
SetQuestion(question){
if (this.questionNumber >= 0){
let oldAnswerButton = document.querySelectorAll('.filter_anwser');
// Deletes old question when the next question is clicked
for (let answerButton of oldAnswerButton) {
answerButton.style.display = 'none';
}
}
this.questionNumber = question;
let q = this.quiz[question];
// Check if your at the last question so the next button will stop being displayed.
if (this.questionNumber === Quiz.length-1) {
this.nextbtn.style.display = 'none';
this.prevbtn.style.display = 'block';
this.resultbtn.style.display = 'grid';
} else if (this.questionNumber === 0 ) {
this.nextbtn.style.display = 'block';
this.prevbtn.style.display = 'none';
this.resultbtn.style.display = 'none';
} else{
this.nextbtn.style.display = 'block';
this.prevbtn.style.display = 'block';
this.resultbtn.style.display = 'none';
}
// Displays Question
this.questionName.textContent = q.questionDescription;
this.questionName.id = "questionID";
return q;
}
NextQuestion() {
let question = this.SetQuestion.bind(this.questionNumber + 1);
// Displays answers of the questions
for (let y = 0; y < question.chosenAnswer.length; y++) {
let item = question.chosenAnswer[y];
// Display answer buttons
let btn = document.createElement('button');
btn.value = item.id;
btn.className = "filter_anwser";
btn.textContent = item.name;
this.button.appendChild(btn);
}
}
PrevQuestion() {
let question = this.SetQuestion(this.questionNumber - 1);
// Displays answers of the questions
for (let y = 0; y < question.chosenAnswer.length; y++) {
let item = question.chosenAnswer[y];
// Display answer buttons
let btn = document.querySelector('button[value="' + item.id + '"]');
btn.style.display = 'block';
console.log(btn);
}
}
/**
* Returns the parameters for the URL.
*
* #returns {string}
*/
getLink() {
let tmp = [];
for (let i = 0; i < this.url.length; i++) {
// Check if question is from the same quiz part and adds a , between chosen answers and add the right prefix at the beginning
if (this.url[i].length > 0){
tmp.push("" + Quiz[i].prefix + this.url[i].join(","))
}
}
/// If answers are from different quiz parts add a & between answers.
console.log(this.url, this.questionNumber);
return "" + tmp.join("&");
};
}
class QuizPart{
constructor(questionDescription, chosenAnswer, prefix){
this.questionDescription = questionDescription;
this.chosenAnswer = chosenAnswer;
this.prefix = prefix;
}
}
class ChosenAnswer{
constructor(id, name){
this.id = id;
this.name = name;
}
}
let Quiz = [
new QuizPart('Whats your size?', [
new ChosenAnswer('6595', '41'),
new ChosenAnswer('6598', '42'),
new ChosenAnswer('6601', '43'),
], 'bd_shoe_size_ids='),
new QuizPart('What color would you like?', [
new ChosenAnswer('6053', 'Red'),
new ChosenAnswer('6044', 'Blue'),
new ChosenAnswer('6056', 'Yellow'),
new ChosenAnswer('6048', 'Green'),
], 'color_ids='),
new QuizPart('What brand would you like?', [
new ChosenAnswer('5805', 'Adidas'),
new ChosenAnswer('5866', 'Nike'),
new ChosenAnswer('5875', 'Puma'),
], 'manufacturer_ids='),
]
let control = new QuestionControl(Quiz);
control.Initialize();
document.getElementById('nextbtn').addEventListener("click", control.NextQuestion);
document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion);
control.button.addEventListener("click", function (e) {
const tgt = e.target;
// clear the url array if there's nothing clicked
if (control.url.length === control.questionNumber) {
control.url.push([]);
}
let quizUrl = control.url[control.questionNumber];
// Check if a button is clicked. Changes color and adds value to the url array.
if (quizUrl.indexOf(tgt.value) === -1) {
quizUrl.push(tgt.value);
e.target.style.backgroundColor = "orange";
// Check if a button is clicked again. If clicked again changes color back and deletes value in the url array.
} else {
quizUrl.splice(quizUrl.indexOf(tgt.value), 1);
e.target.style.backgroundColor = "white";
}
console.log(control.getLink());
})
The problem is that when i started using bind to connect the function SetQuestion to NextQuestion, it gave me the following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'length')
At the for loop in nextQuestion, but if I stop using bind it won't recognize the function SetQuestion. Does anybody know why this happens?
This is not the correct use of bind:
let question = this.SetQuestion.bind(this.questionNumber + 1);
bind returns a function; it doesn't execute it. The argument you pass to bind should be a value to be used for this when eventually that returned function is called.
As this is not the intention here, you should remove .bind here, and just have:
let question = this.SetQuestion(this.questionNumber + 1);
The problem really originates here:
document.getElementById('nextbtn').addEventListener("click", control.NextQuestion);
document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion);
These click handler functions will be called without a specific this value (it will be undefined in strict mode, or else the global object). This is not what you want, since your code expects this to represent an instance of QuestionControl.
This can be solved with bind as follows:
document.getElementById('nextbtn').addEventListener("click", control.NextQuestion.bind(control));
document.getElementById('prevbtn').addEventListener("click", control.PrevQuestion.bind(control));
So it is not as you write:
using bind to connect the function SetQuestion to NextQuestion
bind is used to connect a function to the object that must act as the this value during the execution of the function. In your case that is control.
Alternatively the binding function can be provided explicitly:
document.getElementById('nextbtn').addEventListener("click", () => control.NextQuestion());
document.getElementById('prevbtn').addEventListener("click", () => control.PrevQuestion());
I'm creating a ToDo app in vanilla JavaScript.
My goal is to be able to remove the input data from local storage when the corresponding "X" button has been clicked.
So far, when you click on an X button, the corresponding input field and checkbox are removed from the DOM with this function I created -
function removeToDoInput(button, input1, input2, input3) {
if (allToDoInputs.length > 2) {
button.addEventListener("click", () => {
toDoInputContainer.removeChild(input1);
toDoInputContainer.removeChild(input2);
toDoInputContainer.removeChild(input3);
for (let toDoInput = 0; toDoInput < allToDoInputs.length; toDoInput++) {
for (let i = 0; i < localStorage.length; i++) {
localStorage.removeItem("ToDo " + toDoInput);
console.log("test");
}
}
})
}
}
This works fine. But like I mentioned I also need to remove the corresponding input data from local storage.
Here is the 'add-to-do' button functionality. You'll notice the removeToDoInput is called which I don't think is good -
function createToDoInput() {
const newToDoInputCheckbox = document.createElement("INPUT");
newToDoInputCheckbox.setAttribute("type", "checkbox");
const newToDoInput = document.createElement("INPUT");
const newXBtn = document.createElement("SPAN");
toDoInputContainer.appendChild(newToDoInputCheckbox);
toDoInputContainer.appendChild(newToDoInput);
toDoInputContainer.appendChild(newXBtn);
newToDoInputCheckbox.classList.add("checkbox");
newToDoInput.classList.add("to-do-input");
newXBtn.classList.add("X");
newXBtn.innerHTML = "X";
newXBtn.addEventListener("click", removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn));
}
And here is the save button functionality -
function saveToDoInputs() {
localStorage.setItem("Title", toDoListTitle.value.trim());
for (let toDoInput = 0; toDoInput < allToDoInputs.length; toDoInput++) {
if (createToDoInput) {
localStorage.setItem("ToDo " + toDoInput, allToDoInputs[toDoInput].value.trim());
}
}
}
Both of these last functions I mentioned are attached to buttons through click event listeners.
How can I delete the input from local storage as well as the DOM?
This is incorrect and will be called immediately you assign the event handler
newXBtn.addEventListener("click", removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn));
you need
newXBtn.addEventListener("click", function() { removeToDoInput(newXBtn, newToDoInputCheckbox, newToDoInput, newXBtn)});
or similar
I have redesigned your code and now it
wraps the elements in their own container
delegates click and change to the list container
use relative addressing (closest)
gives the todo a unique ID and stores it in an object
retrieves the object from local storage when loading
adds the object to localStorage when changed
I also added a select all and delete selected
NOTE I have commented the localStorage interaction out because stacksnippets do not allow access to localStorage. Just uncomment them on your page
let todos = {};
// const saveTodos = localStorage.getItem("ToDo");
// if (saveTodos) todos = JSON.parse(saveTodos);
const toDoInputContainer = document.getElementById("toDoInputContainer");
const toggleSelDel = () => {
document.getElementById("seldel").classList.toggle("hide",Object.keys(todos).length===0); // show if there are actual entries - we can toggle on existense of checkboxes instead
}
const saveAndToggle = (id,why) => {
// localStorage.setItem("ToDo",JSON.stringify(todos));
console.log(id, why);
toggleSelDel()
};
const saveTodo = (id, text) => {
todos[id] = text;
saveAndToggle(id,"added")
};
const removeTodo = id => {
if (todos[id]) {
delete todos[id];
}
saveAndToggle(id,"deleted"); // toggle anyway
};
function createToDoInput() {
const todoContainer = document.createElement("div");
todoContainer.id = 'todo' + new Date().getTime(); // unique ID
const newToDoInputCheckbox = document.createElement("INPUT");
newToDoInputCheckbox.setAttribute("type", "checkbox");
const newToDoInput = document.createElement("INPUT");
const newXBtn = document.createElement("SPAN");
todoContainer.appendChild(newToDoInputCheckbox);
todoContainer.appendChild(newToDoInput);
todoContainer.appendChild(newXBtn);
newToDoInputCheckbox.classList.add("checkbox");
newToDoInput.classList.add("to-do-input");
newXBtn.classList.add("X");
newXBtn.innerHTML = "X";
toDoInputContainer.appendChild(todoContainer);
}
toDoInputContainer.addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("X")) {
const parent = tgt.closest("div")
removeTodo(parent.id);
parent.remove();
}
});
toDoInputContainer.addEventListener("change", function(e) {
const tgt = e.target;
if (tgt.classList.contains("to-do-input")) {
const id = tgt.closest("div").id;
if (tgt.value === "") removeTodo(id);
else saveTodo(id, tgt.value);
}
});
document.getElementById("add").addEventListener("click", createToDoInput);
toggleSelDel(); // possibly show the select all button
document.getElementById("selAll").addEventListener("click", function() {
const checked = this.checked;
[...toDoInputContainer.querySelectorAll("[type=checkbox]")].forEach(chk => chk.checked = checked)
});
document.getElementById("delAll").addEventListener("click", function() {
[...toDoInputContainer.querySelectorAll("[type=checkbox]:checked")].forEach(chk => {
const parent = chk.closest("div");
removeTodo(parent.id);
parent.remove();
});
});
.hide { display: none; }
<button id="add">Add</button>
<div id="seldel" class="hide"><label>Select all<input type="checkbox" id="selAll"/></label><button type="button" id="delAll">Delete selected</button></div>
<div id="toDoInputContainer"></div>
I want to be able to press on my trashcan and be able to delete any todo i want. Right now i can only remove the first index off my todo with with the help off splice. Also when i inspect my array in console i want to be able to use true or false. So when i click on my button my object gets removed from my screen and inside off the array in console it should show that my object indeed have been removed and turns into true instead of false( todoIsRemoved: false). here is a link so you can see all off my code: https://jsfiddle.net/marvstarv/tqdzn8bg/.
this is my class:
let p=0;
let allTheToDos = [];
class Todo{
constructor(toDoItem, removedToDo){
this.Id= p ++;
this.toDoItem = toDoItem;
this.removedToDo = removedToDo;
}
}
this is the function i need help with,
function removeTask (){
let liContainer = document.getElementById ("mySection"); // contains my label(checkmark), Li, and trashcan button.
allTheToDos.splice(Todo,1);
liContainer.innerHTML="";
generateHtml();
console.log(allTheToDos);
}
this function is conected to line 64 off my "main.js": deleteButton.addEventListener('click', () =>{removeTask(allTheToDos[i])});
i appriciate all the help, get back to me if anything was unclear.enter code here
I updated your Js file to:
window.onload = function(){
// without this my site keeps realoding when adding a new item
let firstTask = new Todo ('Bädda sängen',false);
let secondTask = new Todo ('Hänga upp tavlorna',false);
let thirdTask = new Todo ('Kick back & realx',false);
// Adding my premade todo's into my Array that has the variable 'allTheToDos'
allTheToDos.push(firstTask);
allTheToDos.push(secondTask);
allTheToDos.push(thirdTask);
// creating a function so that the user can add a new todo
let addButton = document.getElementById('addBtn');
addButton.addEventListener('click',addNewTask);
generateHtml ();
// let checkedLi = document.getElementById('listItem')
// checkedLi.addEventListener('click',)
console.log(allTheToDos);
}
// my puublic
let p=0;
let allTheToDos = [];
class Todo{
constructor(toDoItem, removedToDo){
this.Id= p ++;
this.toDoItem = toDoItem;
this.removedToDo = removedToDo;
}
}
function generateHtml (){
// Creating an Ul for my items
let section = document.getElementById('mySection');
let myUl = document.createElement('ul');
myUl.className = 'listContainer';
section.appendChild(myUl);
// Creating the loop for my premade todo objects
for(i=0; i<allTheToDos.length; i++){
// Create a div wrapper for my li
let myListWrapperItemContainer = document.createElement('div');
myListWrapperItemContainer.className = "listItemsWrapper";
let id = `to_do_${i}`;
myListWrapperItemContainer.id = id;
// Creating Checked button
let checkedIcon = document.createElement('label');
checkedIcon.className = 'checkedIcon listItemsIcon';
checkedIcon.innerHTML = '<i class="fas fa-check"></i>';
//Creating li
let myLi = document.createElement("li");
myLi.classList = "listItem lineTrough";
myLi.id= "listItem";
// Creating delete button
let deleteButton = document.createElement('button');
deleteButton.id ="deleteButton";
deleteButton.className = 'trashCan listItemsIcon';
deleteButton.innerHTML = '<i class="fas fa-trash-alt"></i>';
// OnClick
deleteButton.addEventListener('click', () => {removeTask(id)});
// Adding everything to my html
myListWrapperItemContainer.appendChild(checkedIcon);
myListWrapperItemContainer.appendChild(myLi);
myListWrapperItemContainer.appendChild(deleteButton);
myLi.innerHTML = allTheToDos[i].toDoItem;
myUl.appendChild(myListWrapperItemContainer);
}
}
function addNewTask (stopRefresh){
stopRefresh.preventDefault();
let liContainer = document.getElementById ("mySection");
let inputValue = document.getElementById('textBox').value;
liContainer.innerHTML="";
if (inputValue == ""){
alert("Type in something");
generateHtml();
}
else{
let newInputValue = new Todo (inputValue);
allTheToDos.push(newInputValue);
generateHtml();
}
}
function removeTask (id){
let element = document.getElementById(id);
let index = allTheToDos.findIndex(e => e.toDoItem === element.textContent);
allTheToDos.splice(index,1);
element.parentNode.removeChild(element);
}
I have a button that I am making completely out of JS.
I want to save whether it was in a true or false state so that if a user leaves the page the button will be in the same state when they return. I'd like to use localStorage but I'm relatively new to JS so they may be a better solution.
Current Code:
var btn;
var btn = document.createElement("BUTTON");
btn.onclick = function() {myFunction()};
btn.style.height = "100%";
btn.style.width = "98%";
btn.style.border = "0px";
btn.innerHTML = "CLICK ME";
btn.id = "toggle";
document.getElementById("button").appendChild(btn);
function myFunction() {
document.getElementById('notepad').classList.toggle('hide'); return false
}
Edit Code:
window.onload = function initBt(){
var buttonState = localStorage.getItem('mybutton');
if ( null !== buttonState )
{
buttonState && document.getElementById('notepad').classList.add('hide');
}
}
var btn;
var btn = document.createElement("BUTTON");
btn.onclick = function() {myFunction()};
btn.style.height = "100%";
btn.style.width = "98%";
btn.style.border = "0px";
btn.innerHTML = "CLICK ME";
btn.id = "toggle";
document.getElementById("button").appendChild(btn);
function myFunction() {
var bt = document.getElementById('notepad');
bt.classList.toggle('hide');
localStorage.setItem('mybutton', bt.classList.contains('hide'))
return false;
}
you can use following functions in youir code to store and retrieve the button state:
localStorage documentation
function store(key, data)
{
localStorage.setItem(key, JSON.stringify(data));
}
function retrieve(key)
{
var data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
}
function remove(key)
{
localStorage.removeItem(key);
}
usage examples:
store('mybutton', buttonState);
var buttonState = retrieve('mybutton');
if ( buttonState !== null )
{
/* set button State*/
}
else
{
/* state has not been saved before, initialise */
}
NOTE since localStorage can handle ONLY string values, we use JSON.stringify when saving to make a string out of data and JSON.parse when retrieving in order to store and retrieve arbitrary data (that are of course JSON valid structures)
For your updated question try the following (it would be more helpfull if the whole html structure was added since some elements are missing, but anyway):
// include the needed localStorage manipulation methods
function store(key, data)
{
localStorage.setItem(key, JSON.stringify(data));
}
function retrieve(key)
{
var data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
}
function remove(key)
{
localStorage.removeItem(key);
}
// create and initialise the button
var btn = document.createElement("BUTTON");
btn.onclick = function() {myFunction()};
btn.style.height = "100%";
btn.style.width = "98%";
btn.style.border = "0px";
btn.innerHTML = "CLICK ME";
btn.id = "toggle";
document.getElementById("button").appendChild(btn);
var buttonState = retrieve('mybutton');
// make sure at this point element with id="notepad" exists on page
if ( null !== buttonState )
{
buttonState && document.getElementById('notepad').classList.add('hide');
}
function myFunction() {
var bt = document.getElementById('notepad');
bt.classList.toggle('hide');
store('mybutton', bt.classList.contains('hide'));
return false;
}
For a flexible caching library for web/browser that can support a variety of storage mechanisms (and also support for server-side php and node.js) check Unicache (ps. I am the author)
There are of course other libraries as well (eg localForage)
var ButtonFarmAtivada = new Array();
function X() {
var tableCol = dom.cn("td"); //cell 0
//create start checkbox button
ButtonFarmAtivada[index] = createInputButton("checkbox", index);
ButtonFarmAtivada[index].name = "buttonFarmAtivada_"+index;
ButtonFarmAtivada[index].checked = GM_getValue("farmAtivada_"+index, true);
FM_log(3,"checkboxFarm "+(index)+" = "+GM_getValue("farmAtivada_"+index));
ButtonFarmAtivada[index].addEventListener("click", function() {
rp_farmAtivada(index);
}, false);
tableCol.appendChild(ButtonFarmAtivada[i]);
tableRow.appendChild(tableCol); // add the cell
}
1) is it possible to create the button inside an array as I'm trying to do in that example? like an array of buttons?
2) I ask that because I will have to change this button later from another function, and I'm trying to do that like this (not working):
function rp_marcadesmarcaFarm(valor) {
var vListID = getAllVillageId().toString();
FM_log(4,"MarcaDesmarcaFarm + vListID="+vListID);
var attackList = vListID.split(",");
for (i = 0; i <= attackList.length; i++) {
FM_log(3, "Marca/desmarca = "+i+" "+buttonFarmAtivada[i].Checked);
ButtonFarmAtivada[i].Checked = valor;
};
};
For number 1) yes, you can.
function createInputButton(type, index) { // um, why the 'index' param?
// also, why is this function called 'createInputButton'
// if sometimes it returns a checkbox as opposed to a button?
var inputButton = document.createElement("input");
inputButton.type = type; // alternately you could use setAttribute like so:
// inputButton.setAttribute("type", type);
// it would be more XHTML-ish, ♪ if that's what you're into ♫
return inputButton;
}
I don't really understand part 2, sorry.