客户端JavaScript发送邮件异常问题排查求助
客户端JS实现邮件发送功能的问题排查与解决
问题背景
尝试用客户端JavaScript实现发送邮件功能,点击按钮后新标签页显示空白(地址栏为about:blank)。将header设为全局变量后,首次点击仍有问题,第二次点击可正常工作,但控制台出现跨域权限报错。
相关代码
HTML
<button type="button" onclick="PrepEmail()">Contact Creator/Maintainer/Provider of This Data via email</button>
JavaScript
function PrepEmail() { var DBinfo, pnt, email, version, header; const ws = new WebSocket('ws://localhost:3000/'); ws.onopen = function() { ws.send("DBInfo"); ws.onmessage = function(e) { if (e.data == -1 || e.data == -2) alert ('Email address for Creator of DataBase not available.'); else { DBinfo = e.data; pnt = DBinfo.indexOf("DBCreatorEmail ="); if (pnt == -1) alert ('Email address for Creator of DataBase not available.'); else { email = getData(DBinfo, pnt); if (email == "not specified") alert ('Email address for Creator of DataBase not available.'); else { header = "mailto:"; header += email + "?subject="; pnt = DBinfo.indexOf("DBName ="); if (pnt == -1) header += "DataBase"; else { header += getData(DBinfo, pnt); pnt = DBinfo.indexOf("DBVersion ="); if (pnt != -1) { version = getData(DBinfo, pnt); header += " (" + version + ")"; } } } } } } } const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload'; window.addEventListener(terminationEvent, (event) => { if (event.persisted === false) { ws.onclose = function () { }; ws.close(); } }); SendEmail(header); } function SendEmail (header) { const windowRef = window.open(header, '_blank'); windowRef.focus(); setTimeout(function() { if(!windowRef.document.hasFocus()) { windowRef.close(); } }, 500); } function getData (DB, p) { var pos1 = DB.indexOf('"', p) + 1; var pos2 = DB.indexOf('"', pos1); var data = DB.substring(pos1, pos2); if (data == "") return ("not specified") else return (data); }
WebSocket返回数据
DBName = "The Real DB" DBVersion = "20230101" DBCreator = "Joe Blow" DBCreatorEmail = "jb@foo.com"
问题现象与报错
- 首次点击按钮:新标签页显示空白,地址栏为
about:blank - 将
header设为全局变量后:首次点击仍空白,第二次点击可正常打开邮件客户端,但控制台报错:
Storage access automatically granted for origin “moz-nullprincipal:{c3a06e0d-3847-421d-b143-df87ff91029d}” on “http://999.999.9.999:8080”. Uncaught DOMException: Permission denied to access property "document" on cross-origin object at SendEmail (http://999.999.9.999:8080/~devdir/Misc.html:65) at setTimeout handler*SendEmail (http://999.999.9.999:8080/~devdir/Misc.html:64) at PrepEmail (http://999.999.9.999:8080/~devdir/Misc.html:56) at onclick (http://999.999.9.999:8080/~devdir/Misc.html:1)
问题原因
- 异步执行顺序错误:WebSocket通信是异步操作,
SendEmail(header)在ws.onmessage回调执行前就被调用,此时header未赋值,window.open传入undefined会打开about:blank。第二次点击时全局变量header已被第一次请求的回调赋值,所以能正常工作。 - 跨域权限限制:
mailto:链接打开的是邮件客户端或浏览器的邮件处理页面,属于跨域上下文,尝试访问windowRef.document会触发浏览器同源策略,导致权限报错。
解决办法
1. 修正异步逻辑,在回调内调用SendEmail
将SendEmail(header)移动到ws.onmessage中header赋值完成的代码块内,确保只有当header正确生成后才执行打开邮件的操作:
function PrepEmail() { var DBinfo, pnt, email, version, header; const ws = new WebSocket('ws://localhost:3000/'); ws.onopen = function() { ws.send("DBInfo"); ws.onmessage = function(e) { if (e.data == -1 || e.data == -2) alert ('Email address for Creator of DataBase not available.'); else { DBinfo = e.data; pnt = DBinfo.indexOf("DBCreatorEmail ="); if (pnt == -1) alert ('Email address for Creator of DataBase not available.'); else { email = getData(DBinfo, pnt); if (email == "not specified") alert ('Email address for Creator of DataBase not available.'); else { header = "mailto:"; header += email + "?subject="; pnt = DBinfo.indexOf("DBName ="); if (pnt == -1) header += "DataBase"; else { header += getData(DBinfo, pnt); pnt = DBinfo.indexOf("DBVersion ="); if (pnt != -1) { version = getData(DBinfo, pnt); header += " (" + version + ")"; } } // 确保header生成后再调用SendEmail SendEmail(header); } } } } } const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload'; window.addEventListener(terminationEvent, (event) => { if (event.persisted === false) { ws.onclose = function () { }; ws.close(); } }); }
2. 移除跨域敏感的document访问代码
删除SendEmail中检查windowRef.document.hasFocus()的逻辑,避免触发跨域权限限制:
function SendEmail (header) { const windowRef = window.open(header, '_blank'); if (windowRef) { windowRef.focus(); } else { alert('无法打开邮件客户端,请检查浏览器弹窗设置'); } }
3. 可选:对mailto链接进行URL编码
为避免subject中的特殊字符导致链接失效,对subject部分进行URL编码:
// 生成subject时添加编码 const subject = pnt === -1 ? "DataBase" : `${getData(DBinfo, pnt)}${version ? ` (${version})` : ''}`; header += encodeURIComponent(subject);
内容的提问来源于stack exchange,提问作者Marshall
相关产品推荐
相关产品推荐

