window.open加载事件未触发:PDF下载后无法关闭加载图标
问题:window.open加载PDF下载链接时load事件不触发,无法控制加载图标
我想用window.open打开PDF下载链接前显示加载图标,但没法等下载完成后关闭图标。代码如下:
let window_open = window.open("api/dummyGenerateAdhocTicketPDF?download="+download+"&client_id="+client_id+"&user_id="+user_id+"&manual_ticket_id_array="+manual_ticket_id); window_open.addEventListener('load', function(){console.log("TESTING")}, false);
加载事件从未触发,控制台没输出TESTING,但PDF能正常下载。
编辑补充:
我把window.open的URL换成www.google.com时,load事件正常触发,所以怀疑是Laravel控制器返回下载响应的方式有问题,控制器代码:
return response()->download($tmpFile)->deleteFileAfterSend(true);
原因分析
浏览器收到response()->download()返回的文件下载响应时,不会在新打开的窗口里渲染任何内容——浏览器直接处理下载请求,新窗口要么请求完成后立刻关闭,要么保持空白但不会触发load事件(因为没有实际页面内容加载)。而打开普通网页时,浏览器会加载完整的DOM结构,所以load事件能正常触发。
解决方案
方案一:用Fetch/XMLHttpRequest替代window.open(推荐)
直接发起请求监听完成状态,自己处理文件下载,这样就能精准控制加载图标的显示和隐藏:
// 显示加载图标 showLoadingSpinner(); fetch(`api/dummyGenerateAdhocTicketPDF?download=${download}&client_id=${client_id}&user_id=${user_id}&manual_ticket_id_array=${manual_ticket_id}`, { credentials: 'include' // 如果接口需要登录态,加上这个 }) .then(response => { if (!response.ok) throw new Error('请求失败'); return response.blob(); }) .then(blob => { // 创建临时下载链接 const url = window.URL.createObjectURL(blob); const downloadLink = document.createElement('a'); downloadLink.href = url; downloadLink.download = 'ticket.pdf'; // 自定义下载文件名 document.body.appendChild(downloadLink); downloadLink.click(); // 清理临时资源 window.URL.revokeObjectURL(url); document.body.removeChild(downloadLink); // 关闭加载图标 hideLoadingSpinner(); }) .catch(err => { console.error('下载出错:', err); hideLoadingSpinner(); });
方案二:通过中转页面间接触发下载(保留window.open)
如果一定要用window.open,可以让后端先返回一个中转页面,在页面里触发下载后通知父窗口关闭加载图标:
- 修改Laravel控制器,返回中转视图:
return view('download_redirect', ['filePath' => $tmpFile]);
- 创建
resources/views/download_redirect.blade.php视图文件:
<script> // 触发文件下载 const downloadLink = document.createElement('a'); downloadLink.href = "{{ route('temp.download', ['file' => basename($filePath)]) }}"; // 要先定义一个能访问临时文件的路由 downloadLink.download = 'ticket.pdf'; downloadLink.click(); // 通知父窗口关闭加载图标 if (window.opener && typeof window.opener.hideLoadingSpinner === 'function') { window.opener.hideLoadingSpinner(); } // 延迟关闭当前窗口,确保下载触发完成 setTimeout(() => window.close(), 300); </script>
需要额外定义一个路由来提供临时文件的访问权限,同时注意清理临时文件的逻辑。
内容的提问来源于stack exchange,提问作者Yeo Bryan
相关产品推荐
相关产品推荐

