瀑布流布局与 jQuery Ajax 分页

在 Kayo 的上一篇文章轻图床的新前端与瀑布流布局曾经简略介绍过瀑布流布局,所谓的瀑布流效果就正如轻图床首页效果那样,多个内容相近的栏目紧密排列,尽量使到栏目间的间隙最小(即流体布局),并且随着页面滚动条向下滚动,新的数据会追加至当前页面的尾部直到所有数据加载完毕(滚动触发的 Ajax 翻页)。本文将会介绍如何实现瀑布流布局中的 Ajax 翻页,至于如何实现栏目间的紧密排列,本文将会简略介绍但不在重要的讨论范围。

2013/12/23 更新

轻图床已经全新改版,去掉原有的瀑布流布局,需要参考原有的瀑布流布局效果可以浏览这个另外制作的 Demo(只保留瀑布流翻页和第一张图片的链接,其他的功能入口已经去掉)。

一.实现原理

实现瀑布流布局主要分为两个部分,一是实现流体布局,这里我们采用绝对定位的方式实现,而是滚动触发的 Ajax 分页,这里采用 jQuery 的 Ajax 实现。

第一部分将会采用 Masonry jQuery plugin 。Masonry jQuery plugin 是一款实现流体布局的 jQuery 插件,下文将以轻图床的首页 HTML 为例,给出一个 Masonry 流体布局的实例。第二部分使用 jQuery 的 scroll() 事件触发 Ajax ,触发后使用 jQuery 的 ajax() 方法异步加载新内容,下文将给出完整实例。

二.Masonry 实现的流体布局

假设网站中的 HTML 如下

<div id="content">
	<!-- 包含所有图片的容器 -->
	<div id="thumbs">
		<!-- 各个图片的容器 -->
		<div class="imgbox"></div>
		<div class="imgbox"></div>
		<div class="imgbox"></div>
		<div class="imgbox"></div>
		<div class="imgbox"></div>
	</div>
</div>

Masonry 采用绝对定位进行流体布局,因此还需添加如下的一段 css

#thumbs {position: relative; width: 895px; padding: 10px; }
.imgbox {position: absolute; }

则可以在 js 中添加以下一段 jQuery 代码实现流体布局(需先在 head 中添加 jQuery 的库与 Masonry 的插件库, Kayo 测试 jQuery 库为 1.6.2 的版本)

$(function() {
	// #thumbs 为包含所有图片的容器
    var $container = $('#thumbs');
	// 使用 imagesLoaded() 修复该插件在 chrome 下的问题
    $container.imagesLoaded(function(){
      $container.masonry({
		// 每一列数据的宽度,若所有栏目块的宽度相同,可以不填这段
		columnWidth: 279,
		// .imgbox 为各个图片的容器
        itemSelector : '.imgbox'
      });
    });
});

这样便实现了网页内容的流体布局,不过这仅仅是第一页的流体布局,下面详细介绍如何获取新一页的内容,最后在文末给出完整的瀑布流布局实例。

三.Ajax 分页实现瀑布流

1.通过 Ajax 的方式获取下一页的内容

我们需要网页中具有如下 HTML 结构的导航, next_link 为下一页的 url。

<div id="page_nav">
	<a href="next_link">下一页</a>
</div>

相应的 css

#page_nav {clear: both; text-align: center; }

以下这段代码为通过 Ajax 的方式获取下一页的内容,并追加到当前内容的末尾。

	nextHref = $("#next_page a").attr("href");
	// 给浏览器窗口绑定 scroll 事件
	$(window).bind("scroll",function(){
		// 判断窗口的滚动条是否接近页面底部
        if( $(document).scrollTop() + $(window).height() > $(document).height() - 10 ) {
			// 判断下一页链接是否为空
			if( nextHref != undefined ) {
				// Ajax 翻页
				$.ajax( {
					url: $("#page_nav a").attr("href"),
					type: "POST",
					success: function(data) {
						result = $(data).find("#thumbs .imgbox");
						nextHref = $(data).find("#page_nav a").attr("href");
						$("#page_nav a").attr("href", nextHref);
						$("#thumbs").append(result);
					}
				});
			} else {
				$("#page_nav").remove();
			}
        }
    });

2.对追加的内容进行流体布局

熟悉 jQuery 的童鞋应该会了解 js 对于通过 Ajax 方式插入到页面中的元素并不起作用,但在这里并不需要作出如使用 live() 等处理,因为 Masonry 已经在内部作出类似的处理并且默认起效,因此只需在 Ajax 成功执行后的回调函数中调用 masonry() 方法即可。

$newElems = $result;
$newElems.imagesLoaded(function(){
    $container.masonry( 'appended', $newElems, true );
});

3.对 Ajax 翻页过程作出修饰

在上面的过程中已经有完整的瀑布流布局,但是翻页过程中并没有任何提示,而且直接插入多张图片可能会影响用户体验,因此需要对翻页过程作出一些修饰,下面给出完整代码。

这里需要增加一个如下的元素,用于提示正在加载新内容或提示已到了最后一页。

<div id="page_loading">
	<span>给力加载中……</span>
</div>

相应的 css

#page_loading {display: none; background: #111111; opacity: 0.7; height: 60px; width: 220px;  padding: 10px; position: absolute; bottom: -50px; left: 330px; }

下面是完整的 Ajax 翻页代码

	nextHref = $("#next_page a").attr("href");
	// 给浏览器窗口绑定 scroll 事件
	$(window).bind("scroll",function(){
		// 判断窗口的滚动条是否接近页面底部
        if( $(document).scrollTop() + $(window).height() > $(document).height() - 10 ) {
			// 判断下一页链接是否为空
			if( nextHref != undefined ) {
				// 显示正在加载模块
				$("#page_loading").show("slow");
				// Ajax 翻页
				$.ajax( {
					url: $("#page_nav a").attr("href"),
					type: "POST",
					success: function(data) {
						result = $(data).find("#thumbs .imgbox");
						nextHref = $(data).find("#page_nav a").attr("href");
						$("#page_nav a").attr("href", nextHref);
						$("#thumbs").append(result);
						// 把新的内容设置为透明
						$newElems = result.css({ opacity: 0 });
						$newElems.imagesLoaded(function(){
							$container.masonry( 'appended', $newElems, true );
							// 渐显新的内容
							$newElems.animate({ opacity: 1 });
							// 隐藏正在加载模块
							$("#page_loading").hide("slow");							
						});

					}
				});
			} else {
				$("#page_loading span").text("木有了噢,最后一页了!");
				$("#page_loading").show("fast");
				setTimeout("$('#page_loading').hide()",1000);
				setTimeout("$('#page_loading').remove()",1100);
			}
        }
    });

四.备注

本文给出一种完整的瀑布流布局实现方案,其中重点讲述的 Ajax 翻页以一个较为普遍的实例介绍,该代码可以通用于各种站点的 Ajax 翻页,并不局限于瀑布流布局,并且文中主要讲述布局中的 js 部分,关于后台的实现并没有详述。另外该 Ajax 翻页的代码实例并不是轻图床中的代码,因为轻图床中还有切换为手动导航的功能,因此其功能代码比较复杂,有兴趣可以查看轻图床的 js 源码。

5月10日更新

优化瀑布流布局 https://kayosite.com/lighthumbs-transfer-to-sae-and-improve-cascade-layout.html

本文由 Kayo Lee 发表,本文链接:https://kayosite.com/jquery-ajax-turn-page-and-cascade-layout.html

评论列表

  • 评论者头像
    回复

    技术文章 膜拜 现在懒的折腾

    • 评论者头像
      回复

      @NONO 技术的快乐就是在于常常折腾噢!

    • 评论者头像
      回复

      我试的masonry图片宽度不一的情况下,布局会出现问题,尤其是在使用overflow:hidden隐藏部分图片的情况下
      正在解决中

      • 评论者头像
        回复

        @吉安二手网 可以设置columnWidth固定每列宽度,同时 CSS 控制 img 的宽度为固定宽度!

  • 评论者头像
    回复

    jQuery Ajax不好学

  • 评论者头像
    回复

    o(∩_∩)o 亲。。新春快乐。

  • 评论者头像
    回复

    新春快乐~~!

  • 评论者头像
    回复

    祝你龙年飞腾。

  • 评论者头像
    回复

    这个后后面那片 ajax分页 wordpress 版的是一个效果吧?
    还是?

    • 评论者头像
      回复

      @沫 嗯,这里涉及了布局和分页两个部分,分页的效果和WordPress版的触发分页是一样的效果!

  • 评论者头像
    回复

    请问一下,分页上的 $(data). data是什么类型的?一直获取不到

    • 评论者头像
      回复

      没说清楚,是ajax可以获取到数据,但是无法处理,在JS内处理就是空值。

  • 评论者头像
    回复

    文章写得非常清晰,容易理解.
    有个问题:如果不是分页式可以加载网页内容按屏幕加载吗?

    • 评论者头像
      回复

      @imeng 谢谢Imeng噢!
      按屏幕内容加载新内容其实就相当于分页了,当屏幕滚动到底部时加载下一页的内容,如果你不想显示分页导航,给导航的div设置display:none即可。

  • 评论者头像
    回复

    很想折腾一下,但有个疑问:这样的页面对于SEO是否不利?

    • 评论者头像
      回复

      @开淘网 Ajax对于seo的影响一直讨论不断,瀑布流布局中的Ajax分页只是用于首页分页,我用在自己的图片站上感觉对seo没有什么影响,关注其他方面的seo应该会更加有利了。

  • 评论者头像
    回复

    :lol: :mrgreen: :wink: :wink: :twisted: :cry: :-| :-P

  • 评论者头像
    回复

    请问如何设置一行的文章数 ?比如一行只显示4个文章

    • 评论者头像
      回复

      @cassie 可以设置.masonry的columnWidth参数(本文第3段代码),比如本文例子中为279(单位为px,不用填),如果父容器的宽度为1116px,那么就是一行4篇文章。

  • 评论者头像
    回复

    能做一个完整的例子给予学习参考吗?这个上面的看不懂,能否弄一个可以下载下来学习参考的吗

  • 评论者头像
    回复

    求案例

  • 评论者头像
    回复

    请问一个情况啊,我用masonry测试的时候发现一个这样的情况,页面加载的时候,是等到加载完之后执行masonry,这就有一个问题,当页面加载不快时,会有一个过程用户看到的页面是错乱的,或者说无masonry布局的,只有等到页面加载完之后masonry执行重新渲染页面,才是咱们想要的结果,请问这种情况你是怎么处理的?

    • 评论者头像
      回复

      @吉安二手网 嗯,因为masonry的工作原理是获取img的高和宽,再根据img的高和宽布局,而Chrome是无法在加载img完成前获取其高和宽,因此正如上文代码中那样,为了兼容Chrome而利用.imagesLoaded方法判断图片加载完成后再布局,于是就会出现上面的情况,如果你的用户群不使用Chrome可以删除.imagesLoaded方法,或者改用淘宝 UED 的 KISSY.Waterfall 插件。

      • 评论者头像
        回复

        @Kayo
        我就是了解过kissy才改了解masonry的,kissy目前还不能支持像美丽说或蘑菇街般在右侧或随意哪个位置放广告块,而masonry里一个扩展可以,而我js很菜的说,只能研究大神们的代码自己不会写,我看蘑菇街的代码貌似就是从masonry改过来再扩展的,但他的却不会有这个问题,请问下博主有没有可能用kissy实现在右侧放个广告块,或者帮忙看下蘑菇街怎么解决masonry第一次加载问题,哈,不胜感激

        • 评论者头像
          回复

          @吉安二手网 我之前没有上过美丽说和蘑菇街,刚上了一下,貌似这两个网站没有用到流体布局噢。
          另外在右侧放个广告块具体是怎样?

          • 评论者头像
            回复

            @Kayo 比如说美丽说,他首页没有,但内面用了,如http://www.meilishuo.com/goods,右侧广告块如美丽说这个页面的今日热门活动,其实masonry官网就有一个corner stamp,我想要的就是这个功能,但masonry如前面所说,首次加载时那块我处理不好

            • 评论者头像
              回复

              @吉安二手网 看了美丽说的源码,有用到masonry插件,不过它是怎样改造的我就不清楚了,我给出自己处理首次加载的思路:
              会出现这种情况是因为使用 .imagesLoaded 方法,判断图片加载完毕后才调用 masonry 布局,因此可以先把第一次加载的所有图片设置 display:none , 然后在 .imagesLoaded 方法里调用 masonry 后使用 jQuery show() 方法,即图片加载完毕并且布局后再显示图片,并且你可以再页面中添加一个 gif 图片,在图片显示前用来表示正在加载,图片显示后就用 jQuery 的 hide() 方法把它隐藏,再用代码说明一下吧.
              假设包裹gif图片的div id 为loading,包裹每张图片的div class 为imgbox:

              $(‘.imgbox’).css({display: ‘none’});
              $container.imagesLoaded(function(){
              $container.masonry({
              // 每一列数据的宽度,若所有栏目块的宽度相同,可以不填这段
              columnWidth: 279,
              // .imgbox 为各个图片的容器
              itemSelector : ‘.imgbox’
              });
              // 图片加载完毕,隐藏gif,显示图片
              $(‘#loading’).hide();
              $(‘.imgbox’).show();
              });

      • 评论者头像
        回复

        发现淘宝那个没有获取到KISSY.Waterfall 在HTML层面上没有img而全部都是通过ajax获取 这样基本没有SEO可谈 请问他的有办法改进么 不胜感激

  • 评论者头像

    学习了,效果还不错。

    另外,你的博客很漂亮呀

    • 评论者头像
      回复

      @ 谢谢噢,刚好现在博客换主题了,看看会不会有改进呢!

1 2

回复

你正在以游客身份访问网站,请输入你的昵称和 E-mail