commit 5a85fb24598eabfcd581497e3ad1467dec612321
parent 326d6cb66c96e5828371f023dda2c2c0bdbf1bdf
Author: Hunter
Date: Sat, 7 Mar 2026 16:44:18 -0500
fix event listener leak; ensure unique IDs (fix jumping bug)
Diffstat:
| M | index.html | | | 57 | +++++++++++++++++++++++++++++++++++++-------------------- |
1 file changed, 37 insertions(+), 20 deletions(-)
diff --git a/index.html b/index.html
@@ -454,6 +454,10 @@
return serialized;
}
+ function generateId() {
+ return Date.now().toString(36) + Math.random().toString(36).slice(2)
+ }
+
function loadTasksFromLocalStorage() {
const savedTasks = localStorage.getItem('taskTree');
if (savedTasks) {
@@ -461,7 +465,7 @@
console.log(savedTasks);
return deserializeTaskTree(savedTasks);
} else {
- return { id: 'root', text: 'todo', state: 0, subtasks: [{ id: Date.now(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null };
+ return { id: 'root', text: 'todo', state: 0, subtasks: [{ id: generateId(), text: '', state: 0, subtasks: [] }], selectedSubtaskId: null };
}
}
@@ -476,7 +480,7 @@
const text = line.slice(depth + 2);
const newTask = {
- id: depth === 0 ? 'root' : Date.now() + Math.random(),
+ id: depth === 0 ? 'root' : generateId(),
text: text,
state: status === '_' ? 0 : (status === 'x' ? 1 : 2),
subtasks: [],
@@ -627,16 +631,6 @@
taskInput.addEventListener('keydown', keydownHandler);
taskInput.addEventListener('keyup', keyupHandler);
taskInput.addEventListener('keydown', handleCopyAndCut);
- appContainer.addEventListener('focusin', function(e) {
- if (e.target.tagName === 'INPUT' && e.target.type === 'text') {
- document.querySelectorAll('input[type="text"]').forEach(input => {
- if (input !== e.target) {
- placeCursorAtBeginning(input);
- }
- });
- }
- });
-
taskInput.addEventListener('input', () => {
task.text = taskInput.value;
if (task === currentTask) {
@@ -1059,7 +1053,7 @@
function addNewSubtask(parentTask, currentSubtask = null) {
- const newSubtask = { id: Date.now(), text: '', state: 0, subtasks: [], selectedSubtaskId: null };
+ const newSubtask = { id: generateId(), text: '', state: 0, subtasks: [], selectedSubtaskId: null };
if (currentSubtask) {
const index = parentTask.subtasks.findIndex(t => t.id === currentSubtask.id);
parentTask.subtasks.splice(index + 1, 0, newSubtask);
@@ -1089,18 +1083,30 @@
function navigateTasks(direction) {
const tasks = currentTask.subtasks;
+ const subtaskIndex = tasks.findIndex(t => t.id === currentTask.selectedSubtaskId);
+
+ // Check if the parent task itself is focused
const currentElement = document.activeElement;
- const currentContainer = currentElement.closest('.task-container');
- const currentIndex = Array.from(appContainer.querySelectorAll('.task-container')).indexOf(currentContainer);
+ const currentContainer = currentElement ? currentElement.closest('.task-container') : null;
+ const isParentFocused = currentContainer && currentContainer.dataset.id == currentTask.id;
if (direction === 'up') {
- if (currentIndex > 0) {
- const prevTask = currentIndex === 1 ? currentTask : tasks[currentIndex - 2];
- selectAndFocusTask(prevTask);
+ if (isParentFocused) {
+ // Already at parent, can't go higher
+ } else if (subtaskIndex <= 0) {
+ // At first subtask or not found, go to parent
+ selectAndFocusTask(currentTask);
+ } else {
+ selectAndFocusTask(tasks[subtaskIndex - 1]);
}
} else {
- if (currentIndex < tasks.length) {
- selectAndFocusTask(tasks[currentIndex]);
+ if (isParentFocused) {
+ // Move from parent to first subtask
+ if (tasks.length > 0) {
+ selectAndFocusTask(tasks[0]);
+ }
+ } else if (subtaskIndex >= 0 && subtaskIndex < tasks.length - 1) {
+ selectAndFocusTask(tasks[subtaskIndex + 1]);
}
}
lastSubtaskDownArrowReleased = false;
@@ -1437,6 +1443,17 @@
e.preventDefault();
}
+ // Register focusin handler once (not per-element) to reset cursor on non-focused inputs
+ appContainer.addEventListener('focusin', function(e) {
+ if (e.target.tagName === 'INPUT' && e.target.type === 'text') {
+ document.querySelectorAll('input[type="text"]').forEach(input => {
+ if (input !== e.target) {
+ placeCursorAtBeginning(input);
+ }
+ });
+ }
+ });
+
getThemesFromCSS();
setInitialTheme();
renderCurrentView();