在 jsf web 应用程序中,基于 Javascript 的 session 计时器适用于 Chrome 但不适用于 IE

In a jsf web application, a Javascript based session timer works on Chrome but not on IE

在一个基于 Seam 和 Richfaces 的 jsf web 应用程序中,我 运行 遇到了一个关于不同浏览器的问题。代码(以及我尝试过的每个变体)在 Chrome 中都可以完美运行,但在 Internet Explorer 中却不行(我正在测试版本 11)。

代码应该开始并在 header 中显示 session-timeout 倒计时。在模板文件的开头,超时从应用程序首选项中检索并存储在隐藏字段中。每当加载新页面或触发 AJAX 请求 (resetInactivityTimer()) 时,倒计时计时器都会重置。

我在 IE 中有 2 个问题:

  1. 看来IE上没有触发window.onload功能。当在控制台中手动触发时,计数器开始正常工作。

  2. 手动启动计数器时,触发AJAX请求时出错

    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <title>Title</title>
            <link rel="shortcut icon" type="image/x-icon" href="#{facesContext.externalContext.requestContextPath}/img/favicon.ico"  />
            <a:loadStyle src="/stylesheet/theme.css" />
            <ui:insert name="head" />
        </head>
    
        <body>
            <h:inputHidden id="originalTimeoutId" value="#{preferencesManager.getPreferenceValue(Preference.HTTP_SESSION_TIMEOUT)}"/>
            <a:loadScript src="/scripts/script.js"/>
            <a:region id="status_zone">
                <a:status for="status_zone" forceId="false" id="ajaxStatus" onstart="resetInactivityTimer()">
                         <f:facet name="start">
                            <h:panelGroup>
                                <div style="position: absolute; left: 50%; top: 50%; text-align:center; width: 100%; margin-left: -50%;z-index: 10001;" >
                                    <h:graphicImage value="/img/wait.gif"/>
                                </div>
                                <rich:spacer width="95%" height="95%" style="position: absolute; z-index: 10000; cusor: wait;" />
                            </h:panelGroup>
                         </f:facet>
                </a:status>
                <div class="main">
                    <ui:include src="/layout/header.xhtml" />
                    <ui:include src="/layout/menu.xhtml" />
    
                    <div style="margin-top: 10px;">
                        <ui:insert name="body" />
                    </div>
                    <ui:include src="/layout/footer.xhtml" />
                </div>
            </a:region>
    
            <script type="text/javascript">
                window.onload = initCountdown();
            </script>
        </body>
    </html>
    

倒数计时器显示在 Header 文件 "header.xhtml" 的右上角,该文件已加载到模板中,因此包含在每个页面中:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:s="http://jboss.com/products/seam/taglib"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html">

<div class="header">
    <s:graphicImage value="#{preferencesManager.getPreferenceByteContent(Preference.LOGO)}" styleClass="logo"/>
    <h:panelGrid width="92%" columns="3" columnClasses="headerCol2,headerCol3,headerCol4">

        <h:outputText styleClass="titel"
                      value="#{cM.getStringProp('de.gai_netconsult.kodaba.text.title')}"/>
        <span class="timer">Automatischer Logout in: </span>
        <h:outputText id="counter" styleClass="timer"></h:outputText>

    </h:panelGrid>
</div>

时间放在id="counter"位置。

这是 Javascript 代码:"script.js"

var hiddenField;
var timeoutInSeconds;
var originalTimeout;
var originalCounter;
var initialized = false;


function initCountdown(){
    // quit if this function has already been called
    if (arguments.callee.done) return;

    // flag this function so we don't do the same thing twice
    arguments.callee.done = true;

    // do stuff
    startCountdown();
}


function getHiddenField() {
    if (hiddenField != null) {
        timeoutInSeconds = parseInt(hiddenField.value) * 60;
        return timeoutInSeconds;
    }
    try {
        hiddenField = document.getElementById('originalTimeoutId');
    } catch (e) {
        timeoutInSeconds = 0;
    }
    return timeoutInSeconds;
}

function getOriginalCounter(){
    return document.getElementById('counter');
}

function resetInactivityTimer() {
    if (initialized) {
        console.log("resetInactivityTimer - initialized: " + initialized);
        stopCountdown();
        countdown(timeoutInSeconds, 'counter');
    }
}

function startCountdown () {
    timeoutInSeconds = getHiddenField();
    if(timeoutInSeconds == 0) return;
    originalCounter = getOriginalCounter();
    if(timeoutInSeconds == null || originalCounter == null) {
        setTimeout(function(){
            startCountdown()}, 1000);
        }

    if(timeoutInSeconds != null && originalCounter != null){
        initialized = true;
        originalTimeout = timeoutInSeconds;
        countdown(originalTimeout, 'counter');
    }
}

function stopCountdown() {
    var element = document.getElementById('counter');
    clearTimeout(element.timerId);
}

function leadingzero(number) {
    return (number < 10) ? '0' + number : number;
}

function countdown(seconds, target) {
    var element = document.getElementById(target);
    element.seconds = seconds;
    calculateAndShow('counter');
}

function calculateAndShow(target) {
    var element = document.getElementById('counter');
    if (element.seconds >= 0) {
        element.timerId = window.setTimeout(calculateAndShow,1000,target);
        var h = Math.floor(element.seconds / 3600);
        var m = Math.floor((element.seconds % 3600) / 60);
        var s = element.seconds % 60;
        element.innerHTML=
            leadingzero(h) + ':' +
            leadingzero(m) + ':' +
            leadingzero(s);
        element.seconds--;
    } else {
        completed(target);
        return false;
    }
}

function completed(target) {
    var element = document.getElementById(target);
    element.innerHTML = "<strong>Finished!<\/strong>";
}

我尝试的一些东西正在替换

<script type="text/javascript">
    window.onload = initCountdown();
</script>

<script type="text/javascript">
    if (window.attachEvent) {window.attachEvent('onload', initCountdown());}
    else if (window.addEventListener) {window.addEventListener('load', initCountdown(), false);}
    else {document.addEventListener('load', initCountdown(), false);}
</script>

这导致"Typeconflict"。

或与:

<rich:jQuery name="jcountdown" query="initCountdown()" timing="onload"/>

None 有帮助。

我最终能够让我的计时器工作,我将post我的解决方案在这里:

问题一:

<script type="text/javascript">
    jQuery(document).ready(function(){
       startCountdown();
    });
</script>

而不是 window.onload = startCountdown(); 解决了问题。 重要提示:使用任何console.log()语句时,该函数只会在打开开发者控制台时执行! (F12).

问题二:(AJAX) Richfaces 3.3 版与 IE8 以上的任何 Internet Explorer 版本都不兼容。 应用补丁很重要。这个site详细描述了这个过程。

我还必须对 Javascript 代码进行许多更改。我相信这可以写得更优雅,但我承认我真的 Javascript 一点也不了解......无论如何我正在 post 编写我的代码,以防有人找到它有用:

var hiddenField;
var timeoutInSeconds;
var originalTimeout;
var originalCounter;


function getHiddenField() {
    if (hiddenField != null) {
        timeoutInSeconds = parseInt(hiddenField.value) * 60 -1;
        timeoutInSeconds;
        return timeoutInSeconds;
    }
    try {
        hiddenField = document.getElementById('originalTimeoutId');
    } catch (e) {
        timeoutInSeconds = 0;
    }
    return timeoutInSeconds;
}

function getOriginalCounter(){
    return document.getElementById('counter');
}

function startCountdown () {
    timeoutInSeconds = getHiddenField();
    if(timeoutInSeconds == 0) return;

    originalCounter = getOriginalCounter();

    if(timeoutInSeconds == null || originalCounter == null) {
        setTimeout(function(){
            startCountdown()}, 1000);
        }

    if(timeoutInSeconds != null && originalCounter != null){
        originalTimeout = timeoutInSeconds;
        countdown(originalTimeout, 'counter');
    }
}


function countdown(seconds, target) {
    var element = document.getElementById(target);
    element.seconds = seconds;
    calculateAndShow('counter');
}

function resetCountdown(){
    var element = document.getElementById('counter');
    element.seconds = timeoutInSeconds;
    updateDisplay(element);
}


function calculateAndShow() {
    var element = document.getElementById('counter');
    if (element.seconds > 0) {
        element.timerId = window.setTimeout(calculateAndShow,1000,'counter');
        updateDisplay(element);
        element.seconds--;
    } else {
        completed();
        return false;
    }
}

function updateDisplay(element){
    var h = Math.floor(element.seconds / 3600);
    var m = Math.floor((element.seconds % 3600) / 60);
    var s = element.seconds % 60;
    element.innerHTML =
        leadingzero(h) + ':' +
        leadingzero(m) + ':' +
        leadingzero(s);
}

function leadingzero(number) {
    return (number < 10) ? '0' + number : number;
}

function completed() {
    var element = document.getElementById('counter');
    element.innerHTML = "<strong>Beendet!<\/strong>";
    logoutCallBackToServer();
}

在您的某个 xhtml 文件(模板、页眉、菜单等)中的某处,您还需要添加以下行:

<a4j:jsFunction name="logoutCallBackToServer" immediate="true" action="#{identity.logout}" />

这将确保用户在倒计时达到零时准确注销,以防这与实际会话超时不 100% 匹配。