zoukankan      html  css  js  c++  java
  • ADSajaxj详解(2)检测hash变化解决ajax后退按钮和书签问题

    // 后退按钮和书签功能
    /*
    通过一些DOM脚本并利用URL的hash值可以补救后退按钮和标签的问题。
    其中,hash值表示浏览器应该保持在当前页面上,但同时必须通过重新定位页面使得与hash匹配的命名的锚(或带有相同ID属性的元素),在浏览器的视口(view port)中可见。如果文档中不存在与hash匹配的项,则浏览器只会改变地址栏中的URL。这里的hash可以连同URL一起被作为标签使用,而且通过一些DOM脚本及hack技术,hash还可以在用户单击后退按钮时用于更新页面。
     */
    /**
     * 1.不那么简单的修复
     * 修复后退按钮和变迁涉及监视和识别URL中hash值的变化,并通过该变化来调用Ajax请求。要做到这一点,需要创建一个检测地址中hash值变化的对象,同时以预先定义的适当的方法进行响应。这个对象需要做以下几件事:
     *      (1)是永不唐突的DOM脚本增强文档以跟踪页面的变化。
     *      (2)允许注册根据不同hash值作为反映的不同方法。
     *      (3)监事地址栏的变化并调用注册过的适当方法。
     *      (4)在处理后退按钮和标签时,适应各种不同产品化浏览器的古怪行为。
     * 每种浏览器处理地址的方式都有点不一样:
     *      (1)在IE中,后退和前进按钮会忽略hash值,而只基于URL的其余部分进行导航。为了解决这个问题,需要使用一个隐藏的<iframe>并通过向GET字符串中添加hash来模拟导航到不同的网页。
     *      (2)在Safari中,当通过带hash的URL向前或向后导航时,浏览器的history及history.length会相应改变。但是,window.location.href的值会保持为通过后退和前进按钮导航之前,浏览器中打开的最后一个地址。考虑到安全原因,我们无法访问history中的URL,因此需要相对于Safari的history的长度保持跟踪访问过的hash值,并且能够从存储列表中找回适当的hash。
     *      (3)Firefox和Opera的行为则都更符合常理,他们会在使用后退和前进按钮通过hash来导航时,同步更新window对象的location值。
     *
     * 2.针对产品功能的浏览器嗅探
     * 在处理具体浏览器的产品功能时,浏览器嗅探是一种可以接受的方案,而且也是唯一可能的方案,因为产品的差异并不针对Javascript中的能力或对象。
     *
     * 3.跟踪地质变化
     */
    
    /* 一个用来基于hash触发注册的方法的URL hash侦听器 */
        var actionPager = {
            // 前一个hash
            lastHash: '',
            // 为hash模式注册的方法列表
            callbacks: [],
            // Safari历史记录列表
            safariHistory: false,
            // 对为IE准备的iframe的引用
            msieHistory: false,
            // 应该被转换的链接的类名
            ajaxifyClassName: '',
            // 应用程序的根目录。当创建hash时
            // 它将是被清理后的URL
            ajaxifyRoot: '',
    
            /**
             * 初始化页面(pager)功能
             * @param {String} ajaxifyClass 预定义的类名,只有符合该类名的链接才具备功能,默认为 ADSActionLink
             * @param {String} ajaxifyRoot 预定义的根路径
             * @param {String} startingHash 预定义的hash值,默认为start
             * @example actionPager.init();
             */
            init: function (ajaxifyClass, ajaxifyRoot, startingHash) {
    
                this.ajaxifyClassName = ajaxifyClass || 'ADSActionLink';
                this.ajaxifyRoot = ajaxifyRoot || '';
    
                var ua = navigator.userAgent;
                if (/Safari/i.test(ua) && /Version\/3/.test(ua)) {
                    this.safariHistory = [];
                } else if (/MSIE/i.test(ua) && (parseInt(ua.split('MSIE')[1]) < 8)) {
                    // 如果是MSIE 6/7,添加一个iframe以便
                    // 跟踪重写(override)后退按钮
                    this.msieHistory = document.createElement('iframe');
                    this.msieHistory.setAttribute('id', 'msieHistory');
                    this.msieHistory.setAttribute('name', 'msieHistory');
                    this.msieHistory.setAttribute('src', 'fakepage');
                    setStyleById(this.msieHistory, {
                        'width': '100px',
                        'height': '100px',
                        'border': '1px solid black',
                        'display': 'none',
                        'z-index': -1
                    });
                    document.body.appendChild(this.msieHistory);
                    this.msieHistory = frames.msieHistory;
                }
    
                // 将链接转换为Ajax链接
                this.ajaxifyLinks();
    
                // 取得当前的地址
                var locat = this.getLocation();
    
                // 检测地址中是否包含hash(来自书签)
                // 或者是否已经提供了hash
                if (!locat.hash && !startingHash) {
                    startingHash = 'start';
                }
    
                // 按照需要保存hash
                var ajaxHash = this.getHashFromURL(locat.hash) || startingHash;
                this.addBackButtonHash(ajaxHash);
    
                // 添加监视事件以观察地址栏中的变化
                var watcherCallback = makeCallback(this.watchLocationForChange, this);
    
                if (/MSIE/i.test(ua) && (parseInt(ua.split('MSIE')[1]) < 8)) {
                    window.setInterval(watcherCallback, 200);
                } else {
                    addEvent(window,'hashchange',watcherCallback);
                }
    
            },
    
            /**
             * 会自动将任何带标识的锚标签转换为页面(pager)可以识别的链接
             */
            ajaxifyLinks: function () {
                // 将链接转换为锚以便Ajax进行处理
                var links = getElementsByClassName(this.ajaxifyClassName, document, 'a');
                for (var i = 0, len = links.length; i < len; i++) {
                    var curLink = links[i];
                    if (hasClassName(curLink, 'ADSActionPagerModified')) {
                        continue;
                    }
    
                    // 将href属性转换为#value形式
                    curLink.setAttribute('href', this.convertURLToHash(curLink.getAttribute('href')));
                    addClassName(curLink, 'ADSActionPagerModified');
    
                    // 注册单击事件以便在必要时添加历史纪录
                    addEvent(curLink, 'click', function () {
                        if (this.href && this.href.indexOf('#') > -1) {
                            actionPager.addBackButtonHash(actionPager.getHashFromURL(this.href));
                        }
                    });
                }
            },
    
            /**
             * 记录后退行为
             * @param {String} ajaxHash hash值
             * @return {Boolean}
             */
            addBackButtonHash: function (ajaxHash) {
                // 保存hash
                if (!ajaxHash) {
                    return false;
                }
                if (this.safariHistory) {
                    // 为Safari使用特殊数组
                    if (this.safariHistory.length === 0) {
                        this.safariHistory[window.history.length] = ajaxHash;
                    } else {
                        this.safariHistory[window.history.length + 1] = ajaxHash;
                    }
                    return true;
                } else if (this.msieHistory) {
                    // 在MSIE中通过导航iframe
                    this.msieHistory.document.execCommand('Stop');
                    this.msieHistory.location.href = 'fakepage?hash=' +
                        ajaxHash +
                        '&title=' +
                        document.title;
                    return true;
                } else {
                    // 通过改变地址的值
                    // 使用makeCallback包装函数
                    // 以便在超时方法内部使this
                    // 引用actionPager
                    var timeoutCallback = makeCallback(function () {
                        if (this.getHashFromURL(window.location.href + window.location.hash) !== ajaxHash) {
                            location.replace(location.href + '#' + ajaxHash);
                        }
                    }, this);
                    setTimeout(timeoutCallback, 200);
                    return true;
                }
                return false;
            },
    
            /**
             * 间隔地检测hash的变化,还可执行注册的侦听器
             */
            watchLocationForChange: function () {
                var newHash;
                // 取得新的hash值
                if (this.safariHistory) {
                    // 在Safari中从历史记录数组中取得
                    if (this.safariHistory[history.length]) {
                        newHash = this.safariHistory[history.length];
                    }
                } else if (this.msieHistory) {
                    // 在MSIE中从iframe的地址中取得
                    newHash = this.msieHistory.location.href.split('&')[0].split('=')[1];
                } else if (location.hash !== '') {
                    // 对其他浏览器从window.location中取得
                    newHash = this.getHashFromURL(window.location.href);
                }
    
                // 如果新hash与最后一次的hash不相同,则更新页面
                if (newHash && this.lastHash !== newHash) {
                    if (this.msieHistory && this.getHashFromURL(window.location.href) !== newHash) {
                        // 修复MSIE中的地址栏
                        // 以便能适当地加上标签(或加入收藏夹)
                        location.hash = newHash;
                    }
    
                    // 在发生异常的情况下使用try/catch
                    // 结构尝试执行任何注册的侦听器
                    try {
                        this.executeListeners(newHash);
                        // 在通过处理程序添加任何
                        // 新链接的情况下进行更新
                        this.ajaxifyLinks();
                    } catch (ex) {
                        // 这里将捕获到回调函数中的任何异常JS
                        alert(ex);
                    }
    
                    // 将其保存为最后一个hash
                    this.lastHash = newHash;
                }
            },
    
            /**
             * 用于根据特殊的hash来注册侦听器
             * @param {RegExp || String} regex 正则表达式
             * @param {Function} method 执行的方法
             * @param {Object || Element} context 执行环境,上下文
             */
            register: function (regex, method, context) {
                var obj = {'regex': regex};
                context = context || window;
                // 一个已经指定的环境
                obj.callback = function (matches) {
                    method.apply(context, matches);
                };
    
                // 将侦听器添加到回调函数数则中
                this.callbacks.push(obj);
            },
    
            /**
             * 把链接的URL地址转换为hash值
             * @param {String} url
             * @return {*}
             */
            convertURLToHash: function (url) {
                if (!url) {
                    // 没有url,因而返回一个'#'
                    return '#';
                } else if (url.indexOf('#') > -1) {
                    // 存在hash,因而返回它
                    return url.split('#')[1];
                } else {
                    // ie67会自动添加域名
                    // 如果URL中包含域名(MSIE)则去掉它
                    if (url.indexOf('://') > -1) {
                        var locatH = window.location.href;
                        locatH = locatH.substring(0, locatH.lastIndexOf('/'));
                        var s = '';
                        var len = Math.min(locatH.length, url.length);
                        for (var i = 0; i < len; i++) {
                            if (locatH.charAt(i) !== url.charAt(i)) {
                                break;
                            } else {
                                s += url.charAt(i);
                            }
                        }
    
                        var reg = new RegExp(s + '/');
                        url = url.replace(reg, '');
                        //url = url.match(/:\/\/[^\/]+(.*)/)[1];
                    }
                    // 按照init()约定去掉根目录
                    return '#' + url.substring(this.ajaxifyRoot.length);
                }
            },
    
            /**
             * 从指定的URL中提取出hash值
             * @param url
             * @return {*}
             */
            getHashFromURL: function (url) {
                if (!url || url.indexOf('#') === -1) {
                    return '';
                }
                return url.split('#')[1];
            },
    
            /**
             * 获取当前URL地址
             * @return {*}
             */
            getLocation: function () {
                // 检查hash
                if (!window.location.hash) {
                    // 没有则生成一个
                    var url = {
                        domain: null,
                        hash: null
                    };
                    if (window.location.href.indexOf('#') > -1) {
                        var parts = window.location.href.split('#')[1];
                        url.domain = parts[0];
                        url.hash = parts[1];
                    } else {
                        url.domain = window.location;
                    }
                    return url;
                }
                return window.location;
            },
    
            /**
             * 执行侦听器
             * @param hash
             */
            executeListeners: function (hash) {
                var matches;
                // 执行与hash匹配的任何侦听器
                if (this.callbacks.length) {
                    for (var i in this.callbacks) {
                        if ((matches = hash.match(this.callbacks[i].regex))) {
                            this.callbacks[i].callback(matches);
                        }
                    }
                }
    
            }
        };
    
        window.ADS.actionPager = actionPager;
  • 相关阅读:
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task11
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task10
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task9
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task8
    Datawhale编程实践(LeetCode 腾讯精选练习50)Task7
    Java多线程之三volatile与等待通知机制示例
    Java多线程之一
    [WC2021] 括号路径
    [CF1375H] Set Merging
    [CF1342E] Placing Rooks
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/2888581.html
Copyright © 2011-2022 走看看