perf(comments): lazy-render top-level reply forms
The reply form used to render ~13 lines of hidden markup under every
top-level comment, so a 50-comment video shipped roughly 650 lines of
inert DOM to every signed-in viewer. Replaced with a single
<template id="ytc-reply-form-tpl"> plus a per-comment slot div; the
first click on Reply clones the template, wires the IDs
(replyForm{id}, replyBody{id}, data-parent-id, data-video-id) and
appends it, so toggle behavior stays identical from the user's side.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
8fdac4b483
commit
8aa08e705a
1 file changed
+46
-16
@@ -156,20 +156,12 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Reply form --}}
|
{{-- Reply form is lazy-cloned from #ytc-reply-form-tpl on
|
||||||
|
first click; the ~13-line markup used to render for
|
||||||
|
every top-level comment (so a 50-comment video shipped
|
||||||
|
~650 lines of hidden form markup to every viewer). --}}
|
||||||
@auth
|
@auth
|
||||||
<div class="ytc-reply-form" id="replyForm{{ $comment->id }}">
|
<div class="ytc-reply-slot" data-comment-id="{{ $comment->id }}" data-video-id="{{ $video->getRouteKey() }}"></div>
|
||||||
<img src="{{ Auth::user()->avatar_url }}" class="ytc-avatar ytc-avatar-sm" alt="{{ Auth::user()->name }}">
|
|
||||||
<div class="ytc-input-wrap">
|
|
||||||
<textarea class="ytc-textarea ytc-reply-textarea" id="replyBody{{ $comment->id }}"
|
|
||||||
placeholder="Add a reply..." rows="1"></textarea>
|
|
||||||
<div class="ytc-form-actions">
|
|
||||||
<button class="ytc-btn ytc-btn-cancel _comment-cancel-reply-trigger" data-comment-id="{{ $comment->id }}">Cancel</button>
|
|
||||||
<button class="ytc-btn ytc-btn-submit _comment-submit-reply-trigger"
|
|
||||||
data-video-id="{{ $video->getRouteKey() }}" data-parent-id="{{ $comment->id }}">Reply</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endauth
|
@endauth
|
||||||
|
|
||||||
{{-- Replies --}}
|
{{-- Replies --}}
|
||||||
@@ -1062,10 +1054,29 @@ async function toggleTranslate(btn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Reply form ────────────────────────────────────────
|
// ── Reply form (lazy-rendered from a <template>) ──────
|
||||||
|
// Replaces the per-comment inline markup that used to live in the DOM
|
||||||
|
// pre-hidden. First click on "Reply" clones the template into the comment's
|
||||||
|
// .ytc-reply-slot and wires its IDs. Subsequent toggles just show/hide.
|
||||||
function toggleReplyForm(commentId) {
|
function toggleReplyForm(commentId) {
|
||||||
const form = document.getElementById('replyForm' + commentId);
|
let form = document.getElementById('replyForm' + commentId);
|
||||||
if (!form) return;
|
if (!form) {
|
||||||
|
const slot = document.querySelector('.ytc-reply-slot[data-comment-id="' + commentId + '"]');
|
||||||
|
const tpl = document.getElementById('ytc-reply-form-tpl');
|
||||||
|
if (!slot || !tpl) return;
|
||||||
|
const frag = tpl.content.cloneNode(true);
|
||||||
|
const root = frag.querySelector('.ytc-reply-form');
|
||||||
|
const ta = frag.querySelector('.ytc-reply-textarea');
|
||||||
|
const cancel = frag.querySelector('._comment-cancel-reply-trigger');
|
||||||
|
const submit = frag.querySelector('._comment-submit-reply-trigger');
|
||||||
|
root.id = 'replyForm' + commentId;
|
||||||
|
ta.id = 'replyBody' + commentId;
|
||||||
|
cancel.dataset.commentId = commentId;
|
||||||
|
submit.dataset.parentId = commentId;
|
||||||
|
submit.dataset.videoId = slot.dataset.videoId || YTC.videoId;
|
||||||
|
slot.appendChild(frag);
|
||||||
|
form = root;
|
||||||
|
}
|
||||||
const isOpen = form.classList.contains('open');
|
const isOpen = form.classList.contains('open');
|
||||||
document.querySelectorAll('.ytc-reply-form.open').forEach(f => f.classList.remove('open'));
|
document.querySelectorAll('.ytc-reply-form.open').forEach(f => f.classList.remove('open'));
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
@@ -1513,3 +1524,22 @@ window._comment = {
|
|||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@auth
|
||||||
|
{{-- Reply-form template. Cloned into .ytc-reply-slot the first time a user
|
||||||
|
clicks Reply on a given comment. Keeping it here (not in the layout)
|
||||||
|
means the markup travels with the comments component — the only place
|
||||||
|
that uses it. --}}
|
||||||
|
<template id="ytc-reply-form-tpl">
|
||||||
|
<div class="ytc-reply-form">
|
||||||
|
<img src="{{ Auth::user()->avatar_url }}" class="ytc-avatar ytc-avatar-sm" alt="{{ Auth::user()->name }}">
|
||||||
|
<div class="ytc-input-wrap">
|
||||||
|
<textarea class="ytc-textarea ytc-reply-textarea" placeholder="Add a reply..." rows="1"></textarea>
|
||||||
|
<div class="ytc-form-actions">
|
||||||
|
<button class="ytc-btn ytc-btn-cancel _comment-cancel-reply-trigger">Cancel</button>
|
||||||
|
<button class="ytc-btn ytc-btn-submit _comment-submit-reply-trigger">Reply</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
@endauth
|
||||||
Reference in new issue
Block a user