检测唯一浏览器选项卡

时间:2014-02-03 17:50:49

标签: javascript asp.net browser

在我的网站的每个页面上,我使用AJAX轮询服务器并检索消息列表。服务器维护一个消息列表和SessionId(我在ASP.NET环境中,但我觉得这个问题适用于任何服务器端技术)。如果找到特定SessionId的消息,则将其返回到客户端脚本。我使用JavaScript库来创建通知(使用noty,一个Jquery Notification插件)。一旦它返回特定消息,服务器就会丢弃该消息。

如果用户只为特定网站打开了一个标签/窗口,则效果很好。但是,假设他们有两个打开,他们会做一些导致生成警告消息的事情。我无法控制通知发送到哪个选项卡,因此用户可能不会看到警告消息。

有没有办法唯一识别浏览器标签?然后我可以将它作为我的AJAX调用中的一个参数传递。

4 个答案:

答案 0 :(得分:1)

首先,民意调查似乎不是好机制。当您拥有大量活跃用户时,它可能会关闭您的服务器。理想情况下,您应该在对无效操作导致的请求的响应中返回消息。

仍然低于解决方案可能适合您。它的灵感来自@SergioGarcia的回复。

在表单标记结束之前保留一个隐藏的输入,该表单标记存储唯一ID,用于唯一标识选项卡。您将根据唯一的tabID

将消息存储在服务器会话中
<input type="hidden" id="hiddenInputTabId" value="<%=getValue()%>" />

然后定义getValue

function string getValue() {
    var v = getValueFormBodyOrAccessValueDirectlyByMakingInput_a_ServerSideControl();
    if (string.IsNullOrWhiteSpace(v)) {
        return Guid.NewId();
    } else {
        return v;
    }
}

因为它是一个隐藏的输入,你应该在POSTed表单体中得到它的值,对于下面代码片段的ajax请求,应该注意在you can access on server side的标题中发送该值。

$.ajaxSetup({
    beforeSend: function(xhr) {
        xhr.setRequestHeader("tabId", $('#hiddenInputTabId').val());
    },
});

在将响应返回到轮询请求时可以检查相同的标头,并且只响应提供的tabId可用的响应消息。

答案 1 :(得分:0)

您可以添加一个名为tabId的查询字符串参数,并使用javascript控制它与制表符的绑定。

下面有一个功能原型:

$(function () {

  // from: https://developer.mozilla.org/en-US/docs/Web/API/Window.location
  function getQueryStringParameter (sVar) {
    return decodeURI(window.location.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(sVar).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
  }

  // from: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
  function newGuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
      return v.toString(16);
    });
  }

  window.tabId = getQueryStringParameter('tabId');

  // tabId not defined, reload the page setting it
  if (!window.tabId) {
    window.tabId = newGuid();
  }

  // on click set the tabId of each link to current page
  $(document).on('click', 'a', function (e) {
    var $this = $(this);
    var newLocation = $(this).attr("href");

    // In page links
    if (newLocation.match(/^#.+$/)) {
      return;
    }

    // Outbound links
    if (newLocation.match(new RegExp("^https?")) && !newLocation.match(new RegExp("^https?://" + window.location.host))) {
      return;
    }

    // Insert tab id
    if (newLocation.match(/(\?|&)tabId=[0-9a-f-]+/)) {
      newLocation.replace(/(\?|&)tabId=[0-9a-f-]+/, (newLocation.indexOf('?') == -1 ? "?" : "&") + "tabId=" + window.tabId);
    } else {
      newLocation += (newLocation.indexOf('?') == -1 ? "?" : "&") + "tabId=" + window.tabId;
    }

    window.location.href = newLocation;
    e.preventDefault();
  });
});

如果您在应用程序中输入页面而未在查询字符串上设置tabId参数,则会将其设置为新的UUID(Guid)。

当页面在查询字符串上有tabId参数时,它会在页面中定义window.tabId变量,您可以在应用程序中使用该变量。

当用户点击您网页中的任何链接时,系统会触发一个javascript事件,系统会重定向链接网址以匹配当前的tabId。右键单击以在新选项卡或中间单击中打开将不会触发该事件,并且用户将被发送到没有查询字符串参数的新选项卡,因此新页面将在该页面中创建新的tabId

您可以在此处看到它:http://codepen.io/anon/pen/sCcvK

答案 2 :(得分:0)

您可以通过加载客户端使用javascript生成唯一标签ID来实现。

我强烈建议您使用一些用于intertab通信的内容,例如intercom.js,它可以从单个选项卡广播消息,只需一个连接到其他每个选项卡。 Intertab与socket.io一起使用,它具有较长的轮询回退,所以它在您当前的系统中也可能是好的。我同意轮询是一个糟糕的选择,你应该使用websockets代替。

如果您在服务器上使用ZMQ,那么在浏览器中您也可以使用NullMQ(对于websockets ofc)。我认为它没有intertab支持,因此您应该制作自己的intertab解决方案以使其工作。编写这样一个系统并不是那么难,你只需要一个公共存储,例如localStorage,但它甚至可以是cookie ......如果你没有存储事件,你必须ping该存储以进行更改的setInterval。您必须在那里存储消息,以及哪个选项卡广播它们(可能在信号量中)以及它最后一次ping存储的时间。之后,您可以使每个选项卡与其他选项卡保持同步,或者使用唯一的选项卡ID,您可以将自定义消息发送到任何选项卡。如果广播选项卡有一个存储超时(它没有长时间ping存储),那么它可能已关闭,因此您应该将广播服务分配给另一个选项卡。

答案 3 :(得分:0)

所以我最终做的是改变我的通知框架的功能,以防止识别唯一标签的需要。在无状态网络上分层信息太难了。

我没有使用Ajax立即将消息传递给客户端,而是将每个页面上的消息构建到List<Message>属性中。在PreRender上,我使用ClientScript.RegisterStartupScript()将其呈现给客户。但是,如果我需要将用户发送到另一个页面,我开始使用Server.Transfer()代替Response.Redirect(),以便保留消息队列。新页面检查旧页面以查看它是否存在以及是否是正确的类型。如果它是正确的类型,我将其转换并从旧页面检索消息队列并将它们添加到新页面的队列中。由于Server.Transfer()未更新客户端上的URL,因此我还添加了一个JavaScript函数,以便在支持的浏览器中手动将状态推送到URL。

我知道我把这个问题的方向与我在问题上的方向略有不同,但我认为我在开始时一直在接近它。