基于Data URI的HTML/JS简易浏览器外部资源加载问题求助
内嵌Data URI浏览器的相对资源路径修复方案
我做了一个纯HTML+JS实现的简易浏览器,可以直接以data URI的形式在宿主浏览器中运行。代码如下:
data:text/html,<head><title>Basic Web Browser</title></head> <h1>Basic Web Browser</h1> Enter Web Address or search terms: <input id="url" type="text"/><input type="button" value="Go" onclick="view();"/><input type="button" onclick="textsearch()" value="Google Search"/><input type="button" onclick="imagesearch()" value="Image Search"/><input type="button" value="Open Google URL" onclick="googleURL();"/><input type="button" value="Edit links" onclick="edit()" id="edit" style="display:none"/><input type="button" value="Iframe" onclick="iframe()"/> <a target="_blank">New Tab</a><br/></p><p style="display:none"><script>window.onerror = function(message, url, linenumber) { alert('JavaScript error: ' + message + ' on line ' + linenumber + ' for ' + url); };</script><script> document.querySelector('a').href = window.location; function iframe() { document.querySelector('div').innerHTML='<iframe src="' + document.getElementById('url').value + '" style="width:100%;height:100%"></iframe>'; }; function textsearch() { document.getElementById('url').value = 'google.com/search?q=' + document.getElementById('url').value; view(); }; function imagesearch() { document.getElementById('url').value = 'google.com/images?q=' + document.getElementById('url').value; view(); }; function redirect() { document.getElementById('url').value = this.href; view(); return false; }; var getLocation = function(href) { var l = document.createElement("a"); l.href = href; return l; }; function edit() { for (var i = 0; i < Array.from(document.querySelector('div').querySelectorAll('a')).length; i++) { var tempURL = Array.from(document.querySelector('div').querySelectorAll('a'))[i]; var tempURLEntered = document.getElementById('url').value; if (tempURLEntered.slice(0,4) != 'http') { tempURLEntered = 'http://' + tempURLEntered; }; if (getLocation(tempURL).hostname.length == 0 && tempURL.href.slice(0,4) != 'data') { tempURL.href = 'http://' + getLocation(tempURLEntered).hostname + tempURL.href; }; if (tempURL.onclick == null) { tempURL.onclick = redirect; } else { tempURL.onclick = ';redirect()'; }; }; for (var i = 0; i < Array.from(document.querySelector('div').querySelectorAll('img')).length; i++) { var tempURL = Array.from(document.querySelector('div').querySelectorAll('img'))[i]; alert(tempURL.src); var tempURLEntered = document.getElementById('url').value; if (tempURLEntered.slice(0,4) != 'http') { tempURLEntered = 'http://' + tempURLEntered; }; if (getLocation(tempURL.src).hostname.length == 0 && tempURL.src.slice(0,4) != 'data') { tempURL.src = 'https://' + getLocation(tempURLEntered).hostname + tempURL.src; }; alert(tempURL.src); }; document.getElementById('edit').style='display:none'; }; function googleURL() { url = decodeURIComponent(url).slice(decodeURIComponent(url).indexOf('url')+6,decodeURIComponent(url).indexOf('sa')-1); document.getElementById('url').value = url; view(); } function parse(data) { document.querySelector('div').innerHTML = data; edit(); document.getElementById('edit').style=''; alert(Array.from(document.querySelector('div').querySelectorAll('img'))); }; function view() { var url = document.getElementById('url').value; if (url.slice(18,21) == 'url') { url = decodeURIComponent(url).slice(decodeURIComponent(url).indexOf('url')+6,decodeURIComponent(url).indexOf('sa')-1); document.getElementById('url').value = url; } fetch('https://corsanywhere.herokuapp.com/' + url).then((response) => response.text()).then((text) => parse(text)); }; </script> <div allowfullscreen></div>
目前这个浏览器能在iOS Safari、Firefox和Chrome中运行,但存在相对路径资源加载失败的问题——比如页面里的<img src="/the/path/to/image.png">这类相对路径无法被正确解析。我之前写了edit()函数尝试修复,但这个函数效率低,只处理了<a>和<img>标签,没法覆盖其他资源标签(比如<script>、<link>、<video>等),更没法处理页面JavaScript里的动态资源加载逻辑。
解决方案
1. 用标准URL API替代自定义路径解析
放弃自己写的getLocation函数,直接用浏览器原生的URL构造函数,它能更可靠地解析绝对/相对路径,处理协议、域名、路径拼接等逻辑。
2. 批量处理所有资源标签
扩展资源处理范围,覆盖所有带路径属性的标签:
<a>:href<img>:src,srcset<script>:src<link>:href(样式表、图标等)<video>/<audio>:src,poster<iframe>:src
3. 监听DOM动态变化
用MutationObserver监听页面容器的DOM变化,自动处理新增的元素,解决JavaScript动态加载资源的问题。
4. 优化路径转换逻辑
基于当前加载页面的完整绝对URL,把所有相对路径(包括根相对路径/xxx、相对路径../xxx)转换为完整的绝对路径。
修改后的核心代码
替换原有的getLocation、edit、parse函数,新增observeDOMChanges函数:
// 替换原getLocation,用原生URL API解析相对路径 function resolveRelativeUrl(baseUrl, relativePath) { try { return new URL(relativePath, baseUrl).href; } catch (e) { return relativePath; // 解析失败则保留原路径 } } // 批量处理所有资源路径 function fixAllResources(baseUrl) { // 处理带href属性的元素(a、link) document.querySelectorAll('div a, div link').forEach(el => { if (el.hasAttribute('href')) { const originalHref = el.getAttribute('href'); // 跳过data URI和已为绝对路径的地址 if (!originalHref.startsWith('data:') && !originalHref.startsWith('http')) { el.setAttribute('href', resolveRelativeUrl(baseUrl, originalHref)); } // 绑定点击跳转逻辑,避免覆盖原有事件 el.addEventListener('click', redirect); } }); // 处理带src属性的元素(img、script、video、audio、iframe) document.querySelectorAll('div img, div script, div video, div audio, div iframe').forEach(el => { if (el.hasAttribute('src')) { const originalSrc = el.getAttribute('src'); if (!originalSrc.startsWith('data:') && !originalSrc.startsWith('http')) { el.setAttribute('src', resolveRelativeUrl(baseUrl, originalSrc)); } } // 处理img的srcset属性 if (el.tagName === 'IMG' && el.hasAttribute('srcset')) { const srcset = el.getAttribute('srcset').split(',').map(item => { const parts = item.trim().split(' '); if (parts.length > 0 && !parts[0].startsWith('data:') && !parts[0].startsWith('http')) { parts[0] = resolveRelativeUrl(baseUrl, parts[0]); } return parts.join(' '); }).join(', '); el.setAttribute('srcset', srcset); } // 处理video/audio的poster属性 if ((el.tagName === 'VIDEO' || el.tagName === 'AUDIO') && el.hasAttribute('poster')) { const originalPoster = el.getAttribute('poster'); if (!originalPoster.startsWith('data:') && !originalPoster.startsWith('http')) { el.setAttribute('poster', resolveRelativeUrl(baseUrl, originalPoster)); } } }); } // 监听DOM变化,自动处理新增元素 function observeDOMChanges(baseUrl) { const container = document.querySelector('div'); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length > 0) { fixAllResources(baseUrl); } }); }); observer.observe(container, { childList: true, subtree: true }); } // 替换原parse函数 function parse(data) { const container = document.querySelector('div'); container.innerHTML = data; // 获取当前页面的完整base URL let baseUrl = document.getElementById('url').value; if (!baseUrl.startsWith('http')) { baseUrl = 'https://' + baseUrl; } // 修复现有资源 fixAllResources(baseUrl); // 启动DOM监听,处理动态加载的内容 observeDOMChanges(baseUrl); document.getElementById('edit').style.display = 'none'; } // 调整redirect函数为事件监听模式 function redirect(e) { e.preventDefault(); const targetUrl = this.getAttribute('href'); document.getElementById('url').value = targetUrl; view(); }
额外优化点
- 移除原
edit函数,用fixAllResources替代,批量处理更高效 - 把原
onclick绑定改为addEventListener,避免覆盖原有事件逻辑 - 增加错误捕获,防止路径解析失败导致页面崩溃
- 处理
srcset这类复杂属性,确保图片资源能正确加载
内容的提问来源于stack exchange,提问作者rando
相关产品推荐
相关产品推荐

