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

如何使用JavaScript在加载PDF的iframe中高亮匹配文本

核心限制

你现在直接用iframe嵌入PDF的写法,原生JavaScript根本实现不了自定义文本高亮,两个绕不开的死穴:

  • 跨域拦截:只要加载的PDF和当前页面不同源,浏览器同源策略会直接锁死iframe内部DOM的访问权限,任何操作内部元素的代码都会抛出权限错误
  • 无可用接口:各浏览器自带的默认PDF阅读器,没有对外开放文本检索、高亮的JS调用能力,既没法主动触发它的高亮功能,也无法自定义高亮样式
可落地实现方案(全浏览器兼容、逻辑完全可控)

不要依赖浏览器自带的PDF阅读器,直接用PDF渲染库在自有页面内完成PDF渲染和高亮逻辑,所有代码都运行在自己站点下,不存在跨域问题,样式、交互都可以自定义。
实现逻辑:

  • 移除原有直接加载PDF的iframe,替换为普通div作为PDF渲染容器
  • 将PDF.js相关依赖下载到本地项目引入,加载目标PDF后逐页渲染PDF内容画布和对应的文本层(文本层是实现文本检索、高亮的核心基础,不能省略)
  • PDF全部渲染完成后,遍历所有页面的文本层节点,匹配传入的目标检索文本
  • 给匹配到的文本片段包裹带高亮样式的标签,完成标注

可直接复用的实现代码:

<!DOCTYPE html>
<html>
<head>
  <title>PDF文本高亮示例</title>
  <style>
    /* 自定义高亮样式 */
    .pdf-highlight {
      background: #ffeb3b;
      padding: 2px 0;
      border-radius: 2px;
    }
    #pdf-container {
      width: 100%;
      height: 800px;
      overflow: auto;
      background: #f0f0f0;
    }
    .pdf-page {
      position: relative;
      margin: 10px auto;
      background: #fff;
      box-shadow: 0 2px 8px rgba(0,0,0,0.15);
    }
    /* PDF文本层必备样式,保证文本位置和PDF内容对齐 */
    .textLayer {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      overflow: hidden;
      opacity: 0.2;
      line-height: 1;
    }
    .textLayer > span {
      color: transparent;
      position: absolute;
      white-space: pre;
      cursor: text;
      transform-origin: 0% 0%;
    }
  </style>
</head>
<body>
  <div id="pdf-container"></div>
  <script>
    // 注意:提前将pdf.min.js、pdf.worker.min.js两个依赖文件下载到本地项目存放
    // 替换下方路径为你项目中依赖的实际存放路径,不要引用外部在线资源
    const script = document.createElement('script');
    script.src = '/static/vendor/pdf.min.js';
    script.onload = initPdf;
    document.head.appendChild(script);

    async function initPdf() {
      const pdfjsLib = window['pdfjs-dist/build/pdf'];
      // worker路径同样替换为本地项目路径
      pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/vendor/pdf.worker.min.js';
      
      // 配置项:替换为实际PDF地址、页面传入的待高亮目标文本
      const PDF_URL = 'http://www.africau.edu/images/default/sample.pdf';
      const TARGET_TEXT = 'sample PDF';
      
      // 加载PDF文档
      const pdf = await pdfjsLib.getDocument(PDF_URL).promise;
      const container = document.getElementById('pdf-container');
      
      // 逐页渲染PDF内容和文本层
      for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
        const page = await pdf.getPage(pageNum);
        const scale = 1.5;
        const viewport = page.getViewport({scale: scale});
        
        // 创建单页容器
        const pageDiv = document.createElement('div');
        pageDiv.className = 'pdf-page';
        pageDiv.style.width = `${viewport.width}px`;
        pageDiv.style.height = `${viewport.height}px`;
        
        // 渲染PDF内容画布
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = viewport.width;
        canvas.height = viewport.height;
        pageDiv.appendChild(canvas);
        await page.render({canvasContext: ctx, viewport: viewport}).promise;
        
        // 渲染文本层
        const textLayerDiv = document.createElement('div');
        textLayerDiv.className = 'textLayer';
        pageDiv.appendChild(textLayerDiv);
        const textContent = await page.getTextContent();
        pdfjsLib.renderTextLayer({
          textContentSource: textContent,
          container: textLayerDiv,
          viewport: viewport,
          textDivs: []
        });
        
        container.appendChild(pageDiv);
      }

      // 执行文本匹配高亮
      highlightTargetText(TARGET_TEXT);
    }

    function highlightTargetText(target) {
      if (!target.trim()) return;
      const textLayerList = document.querySelectorAll('.textLayer');
      // 转义正则特殊字符,避免匹配报错
      const reg = new RegExp(target.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
      
      textLayerList.forEach(layer => {
        // 遍历层内所有文本节点
        const walker = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT);
        const textNodes = [];
        let node;
        while (node = walker.nextNode()) {
          textNodes.push(node);
        }
        
        textNodes.forEach(textNode => {
          const content = textNode.textContent;
          reg.lastIndex = 0;
          if (!reg.test(content)) return;
          
          // 拆分匹配节点,包裹高亮标签
          const frag = document.createDocumentFragment();
          let lastIndex = 0;
          let match;
          while ((match = reg.exec(content)) !== null) {
            const matchIndex = match.index;
            if (matchIndex > lastIndex) {
              frag.appendChild(document.createTextNode(content.slice(lastIndex, matchIndex)));
            }
            const highlightSpan = document.createElement('span');
            highlightSpan.className = 'pdf-highlight';
            highlightSpan.textContent = match[0];
            frag.appendChild(highlightSpan);
            lastIndex = matchIndex + match[0].length;
          }
          if (lastIndex < content.length) {
            frag.appendChild(document.createTextNode(content.slice(lastIndex)));
          }
          textNode.parentNode.replaceChild(frag, textNode);
        })
      })
    }
  </script>
</body>
</html>
临时测试用方案(不推荐生产环境使用)

如果只是本地临时测试、不需要兼容多浏览器、也不需要自定义高亮样式,可以直接在iframe的PDF地址后拼接search参数,Chrome、Edge等Chromium内核浏览器会自动触发自带的搜索高亮:

<iframe 
  id="myIFrame" 
  width="100%" 
  height="500px"
  src="http://www.africau.edu/images/default/sample.pdf#page=1&search=替换为要检索的文本"
></iframe>

这个方案缺陷非常明显:

  • Safari、Firefox多数版本不支持该参数,添加后无任何效果
  • 高亮样式为浏览器默认黄色,完全无法自定义
  • 跨域场景下部分浏览器会直接拦截该参数,导致高亮失效
  • 无法通过JS获取匹配结果,也无法监听高亮触发状态

内容的提问来源于stack exchange,提问作者trainingConsultancy USA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 04:54:12