阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      插件 jQuery.address.js 中文API文档

      插件 jQuery.address.js 可以用来制作全站 AJAX (Deep Linking)

      源码 & 下载

      适用场景

      为提升用户体验,改善前端页面效果,越来越多的 Web 应用以及企业或个人团体网站,采用了全站动态不刷新页面的方式加载内容,各种加载效果也层出不穷。

      特别是手机网站,对前端的设计更是要求偏高。刷新页面所产生的等待时间和展现方式严重地影响了网站整体的设计效果和使用体验。

      但是正常的Ajax请求不会改变url,浏览器没法保存历史记录,前进 / 后退的功能就用不了了;

      自己想办法解决,当然没问题,onhashchange~iframe这些都可以的;

      当时真正做项目的时候肯定要花你不少时间做兼任和逻辑处理;

      其实有个jQuery有个插件,把这个问题解决了;就是 jQuery.Address.js 插件可以实现最基本的全站 AJAX 动态加载页面内容的功能的方法。

      插件特点

      它支持许多重要功能,包括:

      • 在浏览器或社交网站中添加书签
      • 通过电子邮件或即时消息发送链接
      • 使用主要搜索引擎查找特定内容
      • 利用浏览器历史记录和重新加载按钮

      个人感觉他最牛逼的是:实现全站链接 AJAX 加载页面内容,不刷新页面,不影响搜索引擎收录

      基本用法

      使用jQuery Address plugin很简单,加一个文件,写几句代码:

      <script type="text/javascript" src="jquery.address-1.4.js"></script>
      <script type="text/javascript">
          $.address.state('这里需要基本URL,地址不改变部分,一般为初始化进入页面地址').init(function(event) {
              //插件初始化,一般这里调用 $('.nav a').address(); 实现链接单击监听
          }).change(function(event) {
              //当页面地址更改的时候调用,即#号之后的地址更改
          }).internalChange(function(event) {
              //内部地址更改,即非通过手动更改URL#号后的内容
          }).bind('externalChange',function(event) {
              //外部地址更改,手动修改URL#号后的内容
          });
      </script>
      

      需要提供给 jQuery Address 的有三个常量,分别是:

      var baseurl          = 'http://www.example.com/test/blog',
          request_uri      = '/test/blog',
          request_uri_host = 'http://www.example.com';
      

      当网站根目录处于域名根目录时,三个常量的定义为:

      var baseurl          = 'http://www.example.com',
          request_uri      = '/',
          request_uri_host = 'http://www.example.com';
      

      以上定义这几个常量数据中的斜杠符非常重要,有误将导致通站 AJAX 链接不能工作。

      官方介绍

      纯JavaScript中的基本实现可能如下所示:

      $.address.change(function(event) {  
          // 根据event.value属性执行某些操作,例如:
          // $('#content').load(event.value + '.xml');  
      });  
      $('a').click(function() {  
          $.address.value($(this).attr('href'));  
      });  
      

      该插件还提供了一个jQuery函数,可以通过以下方式直接使用:

      $('a').address();  
      

      上面的代码片段可以使用处理链接值的附加功能进行扩展:

      $('a').address(function() {  
          return $(this).attr('href').replace(/^#/, '');  
      });
      

      默认情况下,插件会自动将相应的JavaScript事件处理程序添加到具有以下格式的rel属性的每个链接:

      <a href="/deep-link" rel="address:/deep-link">Deep link</a> 
      

      源码解析

      1.自定义事件

      Address定义了四个自定义事件(init,change,internalChange,externalChange),

      • 首次载入时,触发init()事件;
      • 其中只要url变化,会触发change()事件;
      • 点击链接自动改变url时,会触发内部变化internalChange()事件;
      • 点击浏览器前进后退时,会触发外部变化externalChange()事件。

      _update:更新器,触发相应的自定义事件

      _update = function(internal) {
          _trigger(CHANGE);
          _trigger(internal ? INTERNAL_CHANGE : EXTERNAL_CHANGE);
          _st(_track, 10);
      },
      

      _trigger:触发器,触发自定义事件,继承自jquery的trigger()方法。事件触发后,返回一系列location信息:

      {
          value: $.address.value(),
          path: $.address.path(),
          pathNames: $.address.pathNames(),
          parameterNames: parameterNames,
          parameters: parameters,
          queryString: $.address.queryString()
      }
      

      2.检测hash变化,此处要考虑浏览器兼容性

      IE6、7要定时检测hash变化,其他浏览器用window.onhashchange监测。

      if (!_supportsState()) {
          if ((_msie && _version > 7) || (!_msie && ('on' + HASH_CHANGE) in _t)) {
              if (_t.addEventListener) {
                  _t.addEventListener(HASH_CHANGE, _listen, FALSE);
              } else if (_t.attachEvent) {
                  _t.attachEvent('on' + HASH_CHANGE, _listen);
              }
          } else {
              _si(_listen, 50);
          }
      }
      
      listen = function() {
          if (!_silent) {
              var hash = _href(),
                  diff = _value != hash;
              if (diff) {
                  if (_msie && _version < 7) {
                      _l.reload();
                  } else {
                      if (_msie && _version < 8 && _opts.history) {
                          _st(_html, 50);
                      }
                      _value = hash;
                      _update(FALSE);
                  }
              }
          }
      }
      

      支持HTML5的浏览器,引用了新的API,就是history.pushState和history.replaceState,通过这个接口可以做到无刷新改变页面URL。

      pushState是将指定的URL添加到浏览器历史里,replaceState是将指定的URL替换当前的URL。

      if (_supportsState()) {
          _h[_opts.history ? 'pushState' : 'replaceState']({}, '', 
          _opts.state.replace(/\/$/, '') + (_value === '' ? '/' : _value));
      }
      

      响应前进、后退操作,window对象上提供了onpopstate事件。

      $(window).bind('popstate', _popstate).bind('unload', _unload);
      _popstate = function() {
          if (_value != _href()) {
              _value = _href();
              _update(FALSE);
          }
      },
      

      IE6、7浏览器要创建iframe/frame,用来保存hash信息:

      if (_msie && _version < 8) {
          var frameset = _d.getElementsByTagName('frameset')[0];
          _frame = _d.createElement((frameset ? '' : 'i') + 'frame');
          if (frameset) {
              frameset.insertAdjacentElement('beforeEnd', _frame);
              frameset[frameset.cols ? 'cols' : 'rows'] += ',0';
              _frame.noResize = TRUE;
              _frame.frameBorder = _frame.frameSpacing = 0;
          } else {
              _frame.style.display = 'none';
              _frame.style.width = _frame.style.height = 0;
              _frame.tabIndex = -1;
              _d.body.insertAdjacentElement('afterBegin', _frame);
          }
          _st(function() {
              $(_frame).bind('load', function() {
                  var win = _frame.contentWindow;                
                  _value = win[ID] !== UNDEFINED ? win[ID] : '';
                  if (_value != _href()) {
                      _update(FALSE);
                      _l.hash = _crawl(_value, TRUE);
                  }
              });
              if (_frame.contentWindow[ID] === UNDEFINED) {
                  _html();
              }
          }, 50);
      }
      
      _html = function() {
          var src = _js() + ':' + FALSE + ';document.open();document.writeln(\'<html><head><title>' + 
              _d.title.replace('\'', '\\\'') + '</title><script>var ' + ID + ' = "' + encodeURIComponent(_href()) + 
              (_d.domain != _l.hostname ? '";document.domain="' + _d.domain : '') + 
              '";</' + 'script></head></html>\');document.close();';
          if (_version < 7) {
              _frame.src = src;
          } else {
              _frame.contentWindow.location.replace(src);
          }
      },
      

      创建的 iframe/frame,包含location的hash信息:

      <iframe style="DISPLAY: none; WIDTH: 0px; HEIGHT: 0px" 
      tabIndex=-1 
      src="javascript:false;document.open();document.writeln('<html><head><title>jQuery Address Events</title><script>var jQueryAddress = &quot;%2Fjquery%2Faddress%2Fsamples%2Fevents%2Fabout&quot;;</script></head></html>');document.close();" jQuery1333892941446="10">
          <html>
              <head>
                  <title>jQuery Address Events</title>
                  <script>var jQueryAddress = "/jquery/address/samples/events/about";</script>
              </head>
              <body>
              </body>
          </html>
      </iframe>
      

      一个真实案例的源码

      主体功能实现程序如下:
      
      /**
       * 标记初始化完成
       * @type {Boolean}
       */
      var is_init = true;
      
      /**
       * 标记浏览器是否支持 Push State
       * @type {Boolean}
       */
      var is_state = ( window.history.pushState !== undefined );
      
      jQuery(document).ready( function() {
      
        jQuery.address
          // 指定 URL 前缀
          .state( baseurl.replace( request_uri_host, '' ) )
          // 初始化
          .init( function() {
            // 绑定所需的链接,此处筛除了 WordPress 内部功能链接、Feed 订阅链接,以及新窗口链接
            jQuery("a[href^='"+baseurl+"']:not([href^='"+baseurl+"/wp-']):not([href$='/feed/']):not([target='_blank'])")
              .address( function() {
                // 返回相对路径
                return jQuery(this).attr('href').replace( request_uri_host, '' );  
              } );
          } )
          // 初始化为当前 URL
          .path( request_uri )
          // 当 URL 发生改变时的事件
          .change( function( event ) {
            // 防止初始化时重复 AJAX 加载当前页面
            if ( is_state && is_init ) {
              is_init = false;
            } else if ( 0 === jQuery.active ) {
              // 当 AJAX 请求队列为空时
              url = jQuery.address.state().replace( /^\/$/, '' ) + event.path;
              // AJAX 加载所点击的页面内容
              my_load_page_ajax( url );
            }
          } );
      
        // 页面初始化 JS 程序
        my_init_after_ajax();
      
      } );
      
      /**
       * 通过 AJAX 获取新页面内容
       * @param {String} url 页面 URL
       */
      function my_load_page_ajax( url ) {
        // 调用 jQuery AJAX
        jQuery.ajax( {
          url: url,
          type: 'GET',
          dataType: 'html',
          beforeSend: function() {
            // 添加 loading 标记类
            jQuery('body').addClass('loading');
          },
          success: function( data, textStatus, jqXHR ) {
            // 滚动至页面顶部
            jQuery('html, body').scrollTop(0);
            // 替换 #all 层内容
            jQuery('#all').children().remove();
            jQuery('#all').append( jQuery( data ).find('#all').children() );
            jQuery.address.title( />([^<]*)<\/title/.exec( data )[1] );
            // 页面初始化 JS 程序
            my_init_after_ajax();
          },
          complete: function( jqXHR, textStatus ) {
            // 去除 loading 标记类
            jQuery('body').removeClass('loading');
          }
        } );
      }
      
      /**
       * 页面初始化 JS 程序
       */
      function my_init_after_ajax() {
        // ... 各种页面初始化 JS 程序
      }
      

      参考引用

      http://www.jcodecraeer.com/a/javascript/2014/1221/2195.html

      https://chon.io/blog/jquery-address-ajax-deep-linking/

      目录
      目录