You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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()"/>&nbsp;<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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:20:16