JavaScript/jQuery:如何确保跨域点击跟踪事件在用户离开页面之前成功?
我正在从我们公司 Intranet 的各个页面实现点击跟踪,以便添加一些急需的众包流行链接功能(“过去 24 小时内您部门中最受欢迎的链接”等)
。 live() 绑定到页面上所有链接元素的 mousedown 事件,过滤该事件,然后在返回 true 之前向后端服务器发出包含各种数据的伪 ajax 请求,以便触发链接操作
$("#contentarea a").live("mousedown", function(ev) {
//
// detect event, find closest link, process it here
//
$.ajax({
url: 'my-url',
cache: false,
dataType: 'jsonp',
jsonp: 'cb',
data: myDataString,
success: function() {
// silence is golden -- server does send success JSONP but
// regardless of success or failure, we allow the user to continue
}
});
return true; // allow event to continue, user leaves the page.
}
:您可能从上面猜到,我有几个限制:
- 后端跟踪服务器位于与调用页面不同的子域上。我无法回避这个问题。这就是为什么我使用 JSONP(和 GET)而不是使用 POST 的正确 AJAX。我无法实现 AJAX 代理,因为 Web 服务器没有脚本的出站网络访问权限。
- 这可能不相关,但为了充分披露,内容和脚本位于“主要内容”iframe 内(并且这不会改变。我最终可能会将事件侦听器移动到父框架以监视它的链接和所有子内容,但步骤 1 是让它在“1 个子窗口”的简化情况下正常工作)。父级和子级是同一域。
- 后端是 IIS/ASP(同样,一个约束 - 不要问!),因此我无法立即分叉后端进程或以其他方式终止响应,但可以像在更好的平台上一样继续处理
尽管所有这一切,在大多数情况下,系统都能正常工作——我点击页面上的链接,它们就会非常无缝地出现在数据库中。
然而它并不可靠——对于大量链接,特别是目标设置为“_top”的站外链接,它们不会出现。如果在新选项卡或窗口中打开链接,则注册正常。
我已经排除了脚本错误 - 似乎是:
(a)请求永远不会及时到达后端; ( b
) 请求正在发出,但 ASP 检测到客户端不久后断开连接,并且由于它是 GET 请求,因此不处理它。
我怀疑(b),因为服务器的延迟非常快并且许多链接注册正常。如果我在事件触发后放置一个警报弹出窗口,或者将返回值设置为 false,则单击会注册成功。
关于如何解决这个问题有什么建议(在我无法改变约束的情况下)?我无法使 GET 请求同步,因为它不是真正的 AJAX。
问:如果我向 ASP 发出 POST 请求,效果会更好吗?如果 (b) 是罪魁祸首,那么 POST 与 GET 的行为是否会有所不同?如果是这样,我可以使用隐藏的 iframe/form 来发布数据。然而,我怀疑这会更慢、更笨重,而且可能仍然无法及时完成。我无法监听请求是否完成,因为它是跨域的。
问:我可以在 GET 请求发出后向脚本添加延迟吗?我如何以单线程方式执行此操作?我需要从我的函数返回 true,以确保默认事件最终触发,所以我不能使用 setTimeout()。等待“成功”触发并设置一些变量的紧密循环会起作用吗?我担心这会使事情冻结太多并且响应速度会变慢。我认为 jQuery Delay() 插件也只是一个循环?
或者是其他我没有想到的东西可能是罪魁祸首?
我不需要防弹的可靠性。如果所有链接在 95% 的时间内都同样可捕获,那就没问题了。然而现在,有些链接 100% 的时间都可以捕获,而其他链接却无法捕获——这不会影响我想要实现的目标。
提前致谢。
I'm implementing click tracking from various pages in our corporate intranet in order to add some sorely needed crowd-sourced popular link features ("most popular links in your department in the last 24 hours", etc.)
I'm using jQuery's .live() to bind to the mousedown event for all link elements on the page, filter the event, and then fire off a pseudo-ajax request with various data to a back-end server before returning true so that the link action fires:
$("#contentarea a").live("mousedown", function(ev) {
//
// detect event, find closest link, process it here
//
$.ajax({
url: 'my-url',
cache: false,
dataType: 'jsonp',
jsonp: 'cb',
data: myDataString,
success: function() {
// silence is golden -- server does send success JSONP but
// regardless of success or failure, we allow the user to continue
}
});
return true; // allow event to continue, user leaves the page.
}
As you can probably guess from the above, I have several constraints:
- The back-end tracking server is on a different sub-domain from the calling page. I can't get round this. That's why I am using JSONP (and GET) as opposed to proper AJAX with POST. I can't implement an AJAX proxy as the web servers do not have outbound network access for scripts.
- This is probably not relevant, but in the interest of full disclosure, the content and script is inside a "main content" iframe (and this is not going to change. I will likely eventually move the event listener to the parent frame to monitor it's links and all child content, but step 1 is getting it to work properly in the simplified case of "1 child window"). Parent and child are same domain.
- The back-end is IIS/ASP (again, a constraint -- don't ask!), so I can't immediately fork the back-end process or otherwise terminate the response but keep processing like I could on a better platform
Despite all this, for the most part, the system works -- I click links on the page, and they appear in the database pretty seamlessly.
However it isn't reliable -- for a large number of links, particularly off-site links that have their target set to "_top", they don't appear. If the link is opened in a new tab or window, it registers OK.
I have ruled out script errors -- it seems that either:
(a) the request is never making it to the back-end in time; or
(b) the request is making it, but ASP is detecting that the client is disconnecting shortly afterwards, and as it is a GET request, is not processing it.
I suspect (b), since latency to the server is very fast and many links register OK. If I put in an alert pop-up after the event fires, or set the return value to false, the click is registered OK.
Any advice on how I can solve this (in the context that I cannot change my constraints)? I can't make the GET request synchronous as it is not true AJAX.
Q: Would it work better if I was making a POST request to ASP? If (b) is the culprit would it behave differently for POST vs GET? If so, I could use a hidden iframe/form to POST the data. however, I suspect this would be slower and more clunky, and might still not make it in time. I wouldn't be able to listen to see if the request completes because it is cross-domain.
Q: Can I just add a delay to the script after the GET request is fired off? How do I do this in a single-threaded way? I need to return true from my function, to ensure the default event eventually fires, so I can't use setTimeout(). Would a tight loop waiting for 'success' to fire and set some variable work? I'm worried that this would freeze up things too much and the response would be slowed down. I assume the jQuery delay() plugin is just a loop too?
Or is something else I haven't thought of likely to be the culprit?
I don't need bullet-proof reliability. If all links are equally catchable 95% of the time it is fine. However right now, some links are catchable 100% of the time, while others are uncatchable -- which isn't going to cut it for what I want to achieve.
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我会尝试不同的方法。您可以绑定到不同的事件,例如:
I would try a different approach. You can bind to a different event like:
我会尝试从链接事件处理程序返回 false,记住 URL,并仅在 JSONP 请求成功时导航离开。希望它不会增加太多的延迟。考虑到您在内联网上,这可能没问题。
I would try to return false from the link event handler, remember the URL and navigate away only when JSONP request succeeds. Hopefully it shouldn't add too much latency. Considering you are on the inranet, it might be OK.
解决了!
简短的回答是:没有可靠的方法可以通过 GET 请求来执行此跨域操作。我尝试了各种方法,包括存储事件并尝试稍后重播该事件,以及各种方式的黑客尝试使其发挥作用。
然后我尝试了紧密循环,它们也不可靠。
最后,我只是屈服并使用动态创建的表单来发布结果,并将目标设置为隐藏的 iFrame。
工作可靠——浏览器似乎在继续之前暂停以完成其 POST 请求,并且 ASP 尊重 POST。事实证明它一点也不“笨重”。当然,由于浏览器安全模型,我看不到结果......但在这种情况下并不重要。
我现在后悔自己没有先尝试这个选择。
Solved!
The short answer is: there is no reliable way to do this cross-domain with a GET request. I tried all sorts, including storing the event and trying to replay the event later, and all manner of hacks to try to get that to work.
I then tried tight loops, and they weren't reliable either.
Finally, I just gave in and used a dynamically created form that POSTed the results, with the target set to a hidden iFrame.
That works reliably -- it seems the browser pauses to finish its POST request before moving on, and ASP honours the POST. Turns out it's not 'clunky' at all. Sure, due to the browser security model I can't see the result... but it doesn't matter in this case.
I am now kicking myself that I didn't try that option first.