I am have trouble figuring out how to get this code to work. I am trying to remove a todo item based on which one was clicked. My goal eventually is to delete based on which button was clicked.
Here is my code so far:
import uuidv4 from 'uuid/v4'
let text;
let todos = [];
document.querySelector('#new-todo').addEventListener('keypress', e =>
{
text = e.target.value;
if (e.keyCode === 13 || e.which === 13) {
e.preventDefault();
addTodo(text);
renderTodo();
console.log(todos);
}
});
const addTodo = text =>
{
todos.push(
{
id: uuidv4(),
text
});
}
const renderTodo = () =>
{
let node = document.createElement("p");
let textnode = document.createTextNode(text);
node.appendChild(textnode);
document.getElementById("todos").appendChild(node);
}
document.querySelector('#todos').addEventListener('click', () =>
{
removeTodo(todo.id);
console.log(todos);
});
const removeTodo = id =>
{
const todoIndex = todos.findIndex((todo) => todo.id === id)
if (todoIndex > -1)
{
todos.splice(todoIndex, 1);
}
};
Related
i'm making a to do list with only javascript and css. When i add the task, it creates an article with a h1 and 2 icons. The check icon and delete icon. When i click the check icon, it add the class that changes the text style to line-through. The problem is that it's applying to all h1, and i want it to apply only to one specific h1.
function TaskName() {
window.taskName = {};
window.taskName.handdleClick = () => {
const $Task = document.querySelectorAll("h1");
$Task.forEach((e) => {
e.classList.toggle("task-check");
};
window.taskName.delete = () => {
ItemRemoval();
};
let $Input = document.getElementById("input-item");
let $task = $Input.value;
return /*html*/ `
<article id='task-article'>
<h1 id='task-name'>${$task}</h1>
<img src='images/check 1.png' alt='Completar tarefa' onclick='taskName.handdleClick()' id='check-icon'>
<img src='images/delete.svg' alt='Deletar tarefa' onclick='taskName.delete()' id='delete-icon'>
</article>
`;
}
Your problem starts at the structure of your code:
there's a function called TaskName that does a lot of things: creating HTML, deleting something from somewhere, handling a click event
you use the global namespace (window) to handle things
What do you need, if you want a Todo app?
a list of todos (probably an array)
a function to add a new Todo to the list of todos
a function to remove a Todo item from the list of todos
a function that sets 1 Todo item to done (OK, usually toggle between done and not done)
Here's a snippet that does this:
// list of todos & list manipulation functions
let todos = []
const addTodo = (newTodo, todos) => [...todos, newTodo]
const removeTodo = (idToRemove, todos) => todos.filter(({ id }) => idToRemove != id)
const toggleTodoDone = (idToToggle, todos) => todos.map(({ id, done, ...rest }) => id == idToToggle ? { id, done: !done, ...rest } : { id, done, ...rest })
const getTodoItem = (label) => ({
id: Date.now(),
done: false,
label,
})
// DOM manipulation & event handling
const input = document.getElementById("input-add-todo")
const btnAdd = document.getElementById("btn-add-todo")
const container = document.getElementById("container")
const resetInput = () => input.value = ''
btnAdd.addEventListener('click', function() {
const label = input.value
if (label) {
const newTodo = getTodoItem(label)
todos = addTodo(newTodo, todos)
updateContainer(container, todos)
resetInput()
}
})
const getTodoHtml = (todo) => {
const doneClass = todo.done ? ' done' : ''
return `
<div
class="todo-item${doneClass}"
data-id="${todo.id}"
>
${todo.label} - ${todo.done}
<button class="remove-todo" data-id="${todo.id}">X</button>
</div>
`
}
const getTodoListHtml = (todos) => todos.map(getTodoHtml).join('')
const registerEventHandlers = (container) => {
const els = document.querySelectorAll('.todo-item')
els.forEach(el => el.addEventListener('click', function() {
const id = el.dataset.id
todos = toggleTodoDone(id, todos)
updateContainer(container, todos)
}))
const btns = document.querySelectorAll('.remove-todo')
btns.forEach(btn => btn.addEventListener('click', function(e) {
e.stopPropagation()
const id = btn.dataset.id
todos = removeTodo(id, todos)
updateContainer(container, todos)
}))
}
const updateContainer = (container, todos) => {
container.innerHTML = getTodoListHtml(todos)
registerEventHandlers(container)
}
.todo-item {
cursor: pointer;
}
.todo-item.done {
text-decoration: line-through;
}
<div>
<input type="text" id="input-add-todo" />
<button id="btn-add-todo">ADD TODO</button>
</div>
<div id="container"></div>
I am trying to create an add div button and a delete div button. When you select a certain div and click delete, I want to delete only that key from the object. The issue is when I delete and then try to create a new div, it doesn't create the new divs anymore...Not sure what i'm doing wrong or why it only kind of works.
import "./styles.css";
import {
useEffect,
useState
} from "react";
// The parent component
const App = () => {
const [textBoxDivs, setTextBoxDivs] = useState({});
const addNewTextBox = () => {
const numOfTextBoxDivs = Object.keys(textBoxDivs).length;
console.log(numOfTextBoxDivs, "num");
setTextBoxDivs({
...textBoxDivs,
[`div${numOfTextBoxDivs + 1}`]: {
isSelected: false,
innerText: "text"
}
});
};
const selectItem = (e) => {
const nextState = { ...textBoxDivs
};
Object.keys(nextState).forEach((k) => {
nextState[k].isSelected = false;
});
nextState[e.target.id].isSelected = true;
setTextBoxDivs(nextState);
};
const removeSelectedItem = () => {
const nextState = { ...textBoxDivs
};
if (Object.keys(textBoxDivs).length > 0) {
Object.keys(textBoxDivs).map((key) => {
if (textBoxDivs[key].isSelected) {
delete nextState[key];
return setTextBoxDivs(nextState);
}
return null;
});
}
};
return ( <
div >
<
button onClick = {
() => addNewTextBox()
} >
Click me to create a selectable div <
/button> <
button onClick = {
(e) => removeSelectedItem(e)
} >
Click me to delete a selectable div <
/button> {
Object.keys(textBoxDivs).length > 0 &&
Object.keys(textBoxDivs).map((key, index) => {
return ( <
div style = {
{
border: textBoxDivs[key].isSelected ?
"2px solid green" :
"unset"
}
}
onClick = {
(e) => selectItem(e)
}
key = {
index
}
id = {
key
} >
{
textBoxDivs[key].innerText
} <
/div>
);
})
} <
/div>
);
};
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
The problem in your code in the function addNewTextBox, specifically in the line
[`div${numOfTextBoxDivs + 1}`]: {
Because it does not necessarily mean that your are adding a new line. In this case, you are assigning a value to (div + number), but sometimes that already exists. For example, of you change that line for a truly unique number, such as date, it works:
const addNewTextBox = () => {
const numOfTextBoxDivs = Object.keys(textBoxDivs).length;
console.log(numOfTextBoxDivs, "num");
setTextBoxDivs({
...textBoxDivs,
[`div${new Date().getTime()}`]: {
isSelected: false,
innerText: "text"
}
});
};
Update selectItem() then it works:
const selectItem = (e) => {
const nextState = { ...textBoxDivs, setTextBoxDivs }; // setTextBoxDivs was missing
Object.keys(nextState).forEach((k) => {
nextState[k].isSelected = false;
});
nextState[e.target.id].isSelected = true;
setTextBoxDivs(nextState);
};
when I search, I only want words starting with A or B....Z
Sample:
A Au B Be
Allow August Banner Bemol
August Bemol
Animal
searchFilter = (text) => {
const newData = dizi.filter((item) => {
const listItem = `${item.name.toLowerCase()}`;
const textData = text.toLowerCase();
return listItem.indexOf(textData) > -1;
});
this.setState({
diziler: newData,
});
};
There is a built-in string method .startsWith that you can use. It is case-sensitive, so you'll want to use .toLowerCase() on both the text and the item.name.
searchFilter = (text) => {
this.setState({
diziler: dizi.filter((item) =>
item.name.toLowerCase().startsWith(text.toLowerCase())
);,
});
};
searchFilter = (text) => {
const newData = dizi.filter((item) => {
if (
item.name.toLowerCase().startsWith("a") ||
item.name.toLowerCase().startsWith("b")
) {
const listItem = `${item.name.toLowerCase()}`;
const textData = text.toLowerCase();
return listItem.indexOf(textData) > -1;
}
});
this.setState({
diziler: newData,
});
};
I have a react-table where I'm generating about 100 checkboxes dynamically. I have tried everything I can think of, but checking/unchecking the checkboxes takes a very long time before I can see any changes. It seems to be re-rendering every single checkbox before I can see a tick on the checkbox I just clicked on (the page freezes for about 5-10 secs). I'm using a change handler defined in a wrapper component passing as a prop.
// Generate columns for table
// this is added to the column props of React-Table
function setActionsColumns() {
return rolesActionsList.map((action, index) => {
const actionName = get(action, 'name');
return {
Header: actionName,
accessor: actionName,
Cell: row => {
const objectName = get(row, 'original.name');
let data = formData.permissions.filter(
perm => row.tdProps.rest.action === perm.action
);
let roleData = data[0];
let checked;
if (get(roleData, 'object') === row.tdProps.rest.object) {
checked = get(roleData, 'checked');
}
return (
<Checkbox
key={`${actionName}_${index}`}
name={actionName}
onChange={onCheckboxChange}
data-object={objectName}
checked={checked}
/>
);
},
};
});
}
// Checkbox handler defined in a class component
handleCheckboxChange = (event: SyntheticEvent<HTMLInputElement>) => {
const { roleData } = this.state;
const target = event.currentTarget;
const actionName = target.name;
const checked = target.checked;
const objectName = target.dataset.object.toUpperCase();
const checkedItem = {
action: actionName,
object: objectName,
checked,
};
let checkedItemsList = [];
let isInArray = roleData.permissions.some(perm => {
return perm.action && perm.object === checkedItem.object;
});
if (!isInArray) {
checkedItemsList = [...roleData.permissions, checkedItem];
} else {
checkedItemsList = roleData.permissions.filter(
perm => perm.action !== checkedItem.action
);
}
this.setState({
roleData: {
...this.state.roleData,
permissions: checkedItemsList,
},
});
};
I read this tutorial and try to make an auto complete text box.
Here is part of the code:
import React, {useState} from 'react';
import {SearchInput, SearchUL, SearchLI} from './index.style';
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({suggestions = []}) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState('');
const [isTyping, setIsTyping] = useState(false);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log('on click');
};
// done
const doneTyping = () => {
console.log('done type');
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
// --------
// NOTE: this makes the drop down list disappear, but not able to click the list
// --------
const onBlur = () => {
setShowSuggestions(false);
};
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
};
export default Autocomplete;
Sorry, proxy not allow me to upload image. In summary, when you start typing, if match, there will be a dropdown list. Click on 1 of the item, the search box will be filled with it.
Now image you type half way, then your mouse moves to other areas, so like onBlur, the dropdown list is not able to disappear.
I tried to make a onBlur function
const onBlur = () => {
setShowSuggestions(false);
};
and have something like this:
return (
<>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
//onBlur={onBlur}
/>
{suggestionsListComponent}
</>
);
This time, if I click other areas, the dropdown list is able to hide, but when I do normal typing and select dropdown item, the selected item is not able to go to the search box.
Full code here
I have created a sandbox link for you. Check this: https://codesandbox.io/s/testformik-xgl3w
In this, I have wrapped your component with div and passed a ref. And on componentDidMount I am listening to click on the document and calling the function and if the target clicked is different than the AutoComplete components ( input and suggestion ) I close the suggestion box.
This is the new code:
import React, { useState, useEffect } from "react";
import { SearchInput, SearchUL, SearchLI } from "./index.style";
const inputRef = React.createRef();
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({ suggestions = [] }) => {
const [activeSuggestion, setActiveSuggestion] = useState(0);
const [filteredSuggestions, setFilteredSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const [userInput, setUserInput] = useState("");
const [isTyping, setIsTyping] = useState(false);
const handleOuterClick = e => {
if (!inputRef.current.contains(e.target)) {
setShowSuggestions(false);
}
};
useEffect(() => {
document.addEventListener("click", handleOuterClick);
}, []);
// timer id
let typingTimer;
//
let doneTypingInterval = 1000;
// on change
const onChange = e => {
// input
const userInput = e.currentTarget.value;
// match
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
// set state
setActiveSuggestion(0);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
setUserInput(e.currentTarget.value);
};
// onclick
const onClick = e => {
// set state
setActiveSuggestion(0);
setFilteredSuggestions([]);
setShowSuggestions(false);
setUserInput(e.currentTarget.innerText); //?
console.log("on click");
};
// done
const doneTyping = () => {
console.log("done type");
setIsTyping(false);
};
// key down
const onKeyDown = e => {
if (e.keyCode === 13) {
// 1. enter key
// state
setActiveSuggestion(0);
setShowSuggestions(false);
setUserInput(filteredSuggestions[activeSuggestion]);
} else if (e.keyCode === 38) {
// 2. up arrow key
// no active, out
if (activeSuggestion === 0) {
return;
}
// go up, -1
setActiveSuggestion(activeSuggestion - 1);
} else if (e.keyCode === 40) {
// 3. down arr
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
// go down +1
setActiveSuggestion(activeSuggestion + 1);
} else {
}
};
// key up
const onKeyUp = e => {
// clear old timer
clearTimeout(typingTimer);
// has val, then clean up
if (e.currentTarget.value) {
setIsTyping(true);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
};
// tmp: sugList
let suggestionsListComponent;
// state: show and u-input
if (showSuggestions && userInput) {
// state: filterSug.len
if (filteredSuggestions.length) {
// ul + li
suggestionsListComponent = (
<SearchUL>
{filteredSuggestions.map((suggestion, index) => {
return (
<SearchLI key={suggestion} onClick={onClick}>
{suggestion}
</SearchLI>
);
})}
</SearchUL>
);
} else {
// no
suggestionsListComponent = (
<div>
<em>No suggestions!</em>
</div>
);
}
}
return (
<div ref={inputRef}>
<SearchInput
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={userInput}
isTyping={isTyping}
/>
{suggestionsListComponent}
</div>
);
};
export default Autocomplete;
Hope this helps!
Update your onBlur function using timer, like this:
const onBlurr = () => {
setTimeout(() => setShowSuggestions(false), 500);
};