Bootstrap选项卡与Masonry插件的完美结合
Bootstrap 是最流行的前端框架之一。在你的项目中使用Bootstrap,你就可以很快的实现响应式的网页。
如果你尝试将Masonry和Bootstrap提供的众多JavaScript组件之一的 选项卡组件 一起使用,你将会发现许多讨厌的行为。
我遇到过,而本文主要关注这个问题是什么和你要如何来解决这个问题。
Bootstrap的Tabs
Bootstrap的选项卡组件包括两个关键点:选项卡导航元素和一些内容面板。在页面加载时,第一个面板应用了 .active 类。使这个面板默认是可见的。这个类是通过使用JavaScript来切换面板的可见性,通过选项卡导航触发的事件:如果这个面板现在拥有 .active 类那它是可见的,否则这个面板就是隐藏的。
如果你有一些网页内容最好是在单独的块中而不是挤在一个地方,那这种选项卡组件可能派上用场。
为什么是Maronry?
在一些情况下,在每个面板内的内容是适合被显示在响应式的网格布局内的。例如,一系列的商品,服务和文件夹项目都是可以被显示在网格格式内的内容类型。
然而,如果网格的格子不是相同的高度,那像如你所看到的下面的情况将会发生。
两行之间被一些大的间距撑开,使布局看上去好难看。
这就是Masonry解决问题的时候了。加一些Masonry功能到这个混乱的布局中,然后你的布局会动态的适应屏幕的实际面积,消除所有损坏布局的空白间距。
设置DEMO页面
制作一个示例页面用来展示如何整合Bootstrap的标签页和Masonry并不像期望的那么简单。
本文的演示案例 是基于在Bootstrap网站上可用的起始模板 制作的
每个在选项卡面板中的网格项目都是用 Bootstrap的网格系统 和 缩略图组件 建立的。这里是一个代码片段来解释它的结构:
<div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="http://lorempixel.com/200/200/abstract" alt=""> <div class="caption"> <h3>Thumbnail label</h3> <p>...</p> <p> <a href="#" class="btn btn-primary" role="button">Button</a> <a href="#" class="btn btn-default" role="button">Button</a> </p> </div> </div> </div> <!-- Repeat two more times ... -->
上面的代码创建了一个在大型屏幕上为三列,在小型屏幕上为两列的网格。如果你需要复习一下Bootstrap的网格系统,Syed Fazle Rahman写的理解Bootstrap的网格系统是一篇很好的 文章 。
示例页面中的选项卡组件有如下的HTML结构:
<div role="tabpanel"> <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> <a href="#panel-1" aria-controls="panel-1" role="tab" data-toggle="tab">Panel 1</a> </li> <li role="presentation"> <a href="#panel-2" aria-controls="panel-2" role="tab" data-toggle="tab">Panel 2</a> </li> <li role="presentation"> <a href="#panel-3" aria-controls="panel-3" role="tab" data-toggle="tab">Panel 3</a> </li> <li role="presentation"> <a href="#panel-4" aria-controls="panel-4" role="tab" data-toggle="tab">Panel 4</a> </li> </ul> <!-- Tab panels --> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="panel-1"> <div class="row masonry-container"> <div class="col-md-4 col-sm-6 item"> <!-- Thumbnail goes here --> </div> <div class="col-md-4 col-sm-6 item"> <!-- Thumbnail goes here --> </div> <div class="col-md-4 col-sm-6 item"> <!-- Thumbnail goes here --> </div> ... </div><!--End masonry-container --> </div><!--End panel-1 --> <div role="tabpanel" class="tab-pane" id="panel-2"> <!-- Same as what goes inside panel-1 --> </div><!--End panel-2 --> ... </div><!--End tab-content --> </div><!--End tabpanel -->
这里有一些关于上面代码片段的注意事项:
HTML注释指出了选项卡的关键部件: Nav tabs 标志选项卡的导航部分, Nav panels 标志着内容面板。
选项卡的链接通过它们 href 属性的值连接到相应的 id 属性的值相同的内容面板。例如,有着 href="#panel-1" 的链接打开有着 id=panel-1 的内容面板。
在导航部分的每个锚标签都包含 data-toggle="tab" .这个标记使选项卡组件工作而无需写任何额外的JavaScript.
Masonry的目标元素需要有 .masonry-container 类,这个类适用于包含所有网格项目的包装器 div 元素,还需要应用于每单个网格项目的 .item 类。
要看到Masonry库的全部威力,一定要确保网格项目有不同的高度。例如,删除一个项目的图片,缩短另一个项目的段落,等等。
完整的代码,请在CodePen中查看示例的代码 。
添加Masonry库
你可以在 Masonry官网 上通点击”Download“ 按钮下载 masonry.pkgd.min.js 。
为了避免布局问题,库的作者推荐将Masonry与imagesLoaded插件 一起使用。
Masonry不需要 jQuery 。但是因为Bootstrap的JavaScript组件已经在使用jQuery,以jQuery的方式初始化Masonry我将会使我自己的生活更加美好。
这是我们用jQuery和imagesLoaded初始化Masonry需要的代码段。
var $container = $('.masonry-container'); $container.imagesLoaded( function () { $container.masonry({ columnWidth: '.item', itemSelector: '.item' }); });
上面的代码将包裹所有网格项目的 div 存储在一个叫 $container 的变量中。
接下来,Masonry在 $container 上用两个推荐选项进行初始化。 columnWidth 选项表明一个水平网格的一列的宽度。在这里是通过用单个网格项目的类名来设置单个网格项目的宽度的。 itemSelector 选项表明哪个子元素被用作项目元素。在这里,也设定为单个网格项目。
现在是时候来测试代码了。
哎呀!隐藏的面板怎么了?
在一个不使用Bootstrap选项卡的网页上,上面的代码就像施了魔法。然而,在这种情况下,你很快就会发现一种有趣的行为出现。
首先,它看起来不错,因为默认显示的选项卡面板内的网格是显示正确的:
但是,如果你点击选项卡导航链接显示隐藏的面板的内容,就会发生下面的情况:
查看源码,Masonry已经如预期那样触发了,但是每个项目的位置没有被正确的计算:网格项目都像一副纸牌一样堆在一起。
这还不是全部。调整浏览器窗口的大小会使这些网格项目正确定位自己。
让我们来解决这个布局的错误
因为这个出乎意料的布局错误在点击选项卡的导航链接之后变得更明显,那么让我们更密切的观察Bootstrap选项卡触发的事件。
事件列表 非常的短。如下。
show.bs.tab 触发标签页显示,但是是在新的标签页显示之前
shown.bs.tab 触发标签页显示,在标签页显示之后
hide.bs.tab 在新的标签页将显示的时候触发(因此前一个显示的标签页将被隐藏)
hidden.bs.tab 在一个新的标签页显示之后触发(因此前一个显示的标签页是隐藏的)
因为网格布局弄乱是在标签页已经被显示之后,所以我们去找 shown.bs.tab 事件。我们将这里的代码放置到我们原先代码的下面:
$('a[data-toggle=tab]').each(function () { var $this = $(this); $this.on('shown.bs.tab', function () { $container.imagesLoaded( function () { $container.masonry({ columnWidth: '.item', itemSelector: '.item' }); }); }); });
上面的代码中发生了什么:
jQuery .each() 函数循环遍历每个选项卡导航链接,监听shown.bs.tab事件。在这个事件触发时,对应的面板变成可见的,同时Masonry在所有的图片完成加载后重新初始化。
让我们来测试代码
如果你一直跟着文章操作,直接在您的浏览器中启动您的示例页面,或者试试下面的CodePen示例来看看结果。
你可能也想看一下 完整的示例页面 来测试响应式布局效果。
点击选项卡导航链接,注意这个时候网格项目如何在每个面板中适合均匀。改变浏览器的大小会导致网格项目正确的重新定位自己的位置,并有一个漂亮的动画效果。
就是这样,任务完成!
结论
在这篇文章中我已经展示了如何整合Bootstrap的标签页和Masonry JavaScript库。
这两个脚本都容易使用并且非常强大。然而,将它们两个放到一起你将会面临一些影响隐藏的选项卡的布局漏洞。如上面所示,诀窍就是在每个面板变成可见之后重新初始化Masonry库。