openvibers/openvibe.live Commits

Avatar onerror fallback: capture parentNode before this.remove()

OP OpenVibers committed 17 Aug 2026 · 38ddfbb99b · 3 files, +5 −5

this.remove() detaches the img first, so this.parentNode was null and the
letter fallback threw instead of rendering. Bump app.js/settings.js versions.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Xw6Sf5zzSALw8m93XNu5JE

Changes

ddiff --git a/public/index.html b/public/index.html
iindex 5aa586a1..1c475cba 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -4904,7 +4904,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<!-- Scripts -->
<script src="/js/theme-engine.js?v=4"></script>
<script src="/live-notify.js?v=2" defer></script>
--<script src="/js/app.js?v=118" defer></script>
++<script src="/js/app.js?v=119" defer></script>
<script src="/js/stream-player.js?v=17" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
<script src="/js/emotes.js?v=8" defer></script>
@@@ -4970,7 +4970,7 @@ https://www.101soundboards.com/sounds/12345 150p 2</code></pre>
<script src="/js/broadcast.js?v=25" defer></script>
<script src="/js/call.js?v=3" defer></script>
<script src="/js/voice-channels.js?v=2" defer></script>
--<script src="/js/settings.js?v=10" defer></script>
++<script src="/js/settings.js?v=11" defer></script>
<script src="/js/staff-console.js?v=1" defer></script>
<script src="/js/dashboard-moderation.js?v=5" defer></script>
<script src="/js/ai-viewers.js?v=1" defer></script>
ddiff --git a/public/js/app.js b/public/js/app.js
iindex 8f5a789d..2469f671 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -77,7 +77,7 @@ function channelPath(username, managedStreamIdOrSlug = null) {
function _avatarInner(url, name) {
const letter = ((String(name || '?'))[0] || '?').toUpperCase();
return url
-- ? `<img src="${esc(url)}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block" onerror="this.remove();this.parentNode.textContent='${letter}'">`
++ ? `<img src="${esc(url)}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block" onerror="var p=this.parentNode;this.remove();if(p)p.textContent='${letter}'">`
: letter;
}
@@@ -87,7 +87,7 @@ function _avatarSpan(url, name, color, extraCls) {
const cls = extraCls ? ` ${extraCls}` : '';
const bg = color ? ` style="background:${esc(color)}"` : '';
return url
-- ? `<span class="stream-card-avatar${cls}"${bg}><img src="${esc(url)}" alt="" onerror="this.remove();this.parentNode.textContent='${letter}'"></span>`
++ ? `<span class="stream-card-avatar${cls}"${bg}><img src="${esc(url)}" alt="" onerror="var p=this.parentNode;this.remove();if(p)p.textContent='${letter}'"></span>`
: `<span class="stream-card-avatar${cls}"${bg}>${letter}</span>`;
}
ddiff --git a/public/js/settings.js b/public/js/settings.js
iindex 25ac7818..1f7f15d0 100644
---- a/public/js/settings.js
++++ b/public/js/settings.js
@@@ -126,7 +126,7 @@ function _renderSettingsAvatar(u) {
const letter = (u.username || '?')[0].toUpperCase();
const chip = '<span class="settings-avatar-edit"><i class="fa-solid fa-camera"></i></span>';
el.innerHTML = (u.avatar_url
-- ? `<img src="${u.avatar_url}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block" onerror="this.remove();this.parentNode.insertAdjacentText('afterbegin','${letter}')">`
++ ? `<img src="${u.avatar_url}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block" onerror="var p=this.parentNode;this.remove();if(p)p.insertAdjacentText('afterbegin','${letter}')">`
: letter) + chip;
}