From 47f3f9a1f71ac1118eaa663edd54988c5fe2d4b3 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:37:54 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20UI=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20DOM=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HTML 템플릿 내의 `input` 이벤트 리스너 외부에 캐싱 변수를 선언하여 DOM 쿼리 및 문자열 파싱 오버헤드를 줄였습니다. - 관련 테스트 코드의 검증 조건을 수정했습니다. --- .jules/bolt.md | 3 +++ saas_web.py | 32 ++++++++++++++++++++------------ tests/test_saas_web.py | 4 ++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 341c7c9..78b53ea 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -67,3 +67,6 @@ ## 2025-02-12 - [Fast Path Execution in Directory Traversal and Log Parsing] **Learning:** Checking for string existence (`if "silence_" not in stderr`) before invoking regex matchers provides significant speed improvements when parsing large blocks of text. Similarly, moving expensive I/O operations like `os.path.realpath` inside conditional blocks prevents redundant disk access when configuration (like path exclusions) isn't utilized. **Action:** When working on large text processing or disk operations, verify if early exit conditions or conditional execution can bypass the expensive system or library calls. +## 2026-06-25 - [DOM Query and Parsing Optimization in Event Listeners] +**Learning:** Querying the DOM (e.g., `document.querySelectorAll`) and parsing attributes (e.g., `Number.parseInt(btn.dataset.bytes)`) inside frequent event listeners like `input` introduces unnecessary repeated processing overhead on every keystroke. +**Action:** Always precompute static values and cache NodeLists/Elements outside of frequent event handlers to improve client-side UI responsiveness. diff --git a/saas_web.py b/saas_web.py index 3a7b035..9176486 100644 --- a/saas_web.py +++ b/saas_web.py @@ -239,19 +239,23 @@ async def add_security_headers(request: Request, call_next): preview.innerText = 'Selected file size: ' + text; } + const targetBytesPreview = document.getElementById('target_bytes_preview'); + const targetBytesButtons = Array.from(document.querySelectorAll('#preset_buttons_container .preset-btn')).map(btn => ({ + el: btn, + val: Number.parseInt(btn.dataset.bytes, 10) + })); + document.getElementById('target_bytes').addEventListener('input', function(e) { const val = parseInt(this.value, 10); - const preview = document.getElementById('target_bytes_preview'); + const preview = targetBytesPreview; this.setCustomValidity(''); this.removeAttribute('aria-invalid'); preview.style.color = '#1e7e34'; - const buttons = document.querySelectorAll('#preset_buttons_container .preset-btn'); - buttons.forEach(btn => { - const presetValue = Number.parseInt(btn.dataset.bytes, 10); - btn.setAttribute( + targetBytesButtons.forEach(btn => { + btn.el.setAttribute( 'aria-pressed', - !e.isTrusted && presetValue === val ? 'true' : 'false' + !e.isTrusted && btn.val === val ? 'true' : 'false' ); }); @@ -266,19 +270,23 @@ async def add_security_headers(request: Request, call_next): }); + const batchTargetBytesPreview = document.getElementById('batch_target_bytes_preview'); + const batchTargetBytesButtons = Array.from(document.querySelectorAll('#batch_preset_buttons_container .preset-btn')).map(btn => ({ + el: btn, + val: Number.parseInt(btn.dataset.bytes, 10) + })); + document.getElementById('batch_target_bytes').addEventListener('input', function(e) { const val = parseInt(this.value, 10); - const preview = document.getElementById('batch_target_bytes_preview'); + const preview = batchTargetBytesPreview; this.setCustomValidity(''); this.removeAttribute('aria-invalid'); preview.style.color = '#1e7e34'; - const buttons = document.querySelectorAll('#batch_preset_buttons_container .preset-btn'); - buttons.forEach(btn => { - const presetValue = Number.parseInt(btn.dataset.bytes, 10); - btn.setAttribute( + batchTargetBytesButtons.forEach(btn => { + btn.el.setAttribute( 'aria-pressed', - !e.isTrusted && presetValue === val ? 'true' : 'false' + !e.isTrusted && btn.val === val ? 'true' : 'false' ); }); diff --git a/tests/test_saas_web.py b/tests/test_saas_web.py index 57b879d..ef92bc1 100644 --- a/tests/test_saas_web.py +++ b/tests/test_saas_web.py @@ -331,8 +331,8 @@ def test_get_ui_includes_preset_buttons(self): self.assertIn('aria-pressed="false"', html) self.assertIn('role="group" aria-label="Preset target sizes"', html) self.assertNotIn('onclick="setTargetBytes(', html) - self.assertIn("const presetValue = Number.parseInt(btn.dataset.bytes, 10);", html) - self.assertIn("!e.isTrusted && presetValue === val", html) + self.assertIn("val: Number.parseInt(btn.dataset.bytes, 10)", html) + self.assertIn("!e.isTrusted && btn.val === val", html) self.assertNotIn("btn.dataset.bytes === this.value", html) From c30d26ee52638a4609f34e467ab0ceea2dd516f9 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:18:40 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20UI=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20DOM=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HTML 템플릿 내의 `input` 이벤트 리스너 외부에 캐싱 변수를 선언하여 DOM 쿼리 및 문자열 파싱 오버헤드를 줄였습니다. - 최적화 배경과 목적을 설명하는 주석을 추가했습니다. - 관련 테스트 코드의 검증 조건을 수정했습니다. --- saas_web.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/saas_web.py b/saas_web.py index 9176486..87d6aac 100644 --- a/saas_web.py +++ b/saas_web.py @@ -239,6 +239,8 @@ async def add_security_headers(request: Request, call_next): preview.innerText = 'Selected file size: ' + text; } + // Optimize: Cache DOM references and precompute button values outside the input event listener. + // This prevents repeating O(N) DOM traversal and string parsing on every keystroke. const targetBytesPreview = document.getElementById('target_bytes_preview'); const targetBytesButtons = Array.from(document.querySelectorAll('#preset_buttons_container .preset-btn')).map(btn => ({ el: btn, @@ -270,6 +272,8 @@ async def add_security_headers(request: Request, call_next): }); + // Optimize: Cache DOM references and precompute button values outside the input event listener. + // This prevents repeating O(N) DOM traversal and string parsing on every keystroke. const batchTargetBytesPreview = document.getElementById('batch_target_bytes_preview'); const batchTargetBytesButtons = Array.from(document.querySelectorAll('#batch_preset_buttons_container .preset-btn')).map(btn => ({ el: btn,