// Progressive enhancement for the reference fixture: every form in the app // is submitted with fetch, and the server's re-rendered .todoapp replaces // the current one. Editing in place is client-side until it saves. const FETCH = { 'X-Requested-With': 'fetch' }; async function send(form) { const res = await fetch(form.action, { method: 'POST', headers: FETCH, body: new URLSearchParams(new FormData(form)), }); const doc = new DOMParser().parseFromString(await res.text(), 'text/html'); document.querySelector('.todoapp').replaceWith(doc.querySelector('.todoapp')); } document.addEventListener('submit', async (e) => { const form = e.target; if (!form.closest('.todoapp')) return; e.preventDefault(); // Leave editing mode first, so the focusout that follows doesn't save twice. form.closest('li')?.classList.remove('editing'); const adding = form.querySelector('.new-todo'); await send(form); if (adding) document.querySelector('.new-todo').focus(); }); document.addEventListener('change', (e) => { if (e.target.matches('.toggle, .toggle-all')) e.target.form.requestSubmit(); }); document.addEventListener('dblclick', (e) => { const label = e.target.closest('.todo-list label'); if (!label) return; const li = label.closest('li'); li.classList.add('editing'); const input = li.querySelector('.edit'); input.focus(); input.setSelectionRange(input.value.length, input.value.length); }); document.addEventListener('keydown', (e) => { if (!e.target.matches('.edit')) return; const li = e.target.closest('li'); if (e.key === 'Escape') { e.target.value = li.querySelector('label').textContent; li.classList.remove('editing'); } }); document.addEventListener('focusout', (e) => { if (!e.target.matches('.edit')) return; const li = e.target.closest('li'); if (li.classList.contains('editing')) { li.classList.remove('editing'); e.target.form.requestSubmit(); } });