本帖最后由 老邮局 于 2015-7-23 20:50 编辑
我们来看"活动"页面,布局没什么好说的,我们主要看下header部分的两个下拉菜单: 下拉菜单其实是一个新的窗口,背景色为灰色半透,上半部分是不透的ul和li标签。 1、header默认样式 2、下拉菜单的逻辑与实现 3、下拉菜单的数据 4、更新header与对应下拉菜单项数据 5、选择后关闭下拉菜单
1、header默认样式 在第一篇我们说过了,所有header都在index.html定义好了,默认只显示一个,当底部导航栏切换后,header会对应的切换显示。 index.html
- 28 <div class="activity header">
- 29 <h1>玩转晋城</h1>
- 30 <ul class="submenu">
- 31 <li><a tapmode="" onclick="searchAct(this,'city')" class="city"><i></i><span>全城</span></a></li>
- 32 <li><a tapmode="" onclick="searchAct(this,'type')" class="hot"><i></i><span>全部类型</span></a></li>
- 33 </ul>
- 34 </div>
复制代码
2、下拉菜单的逻辑与实现
此时当用户点击任何一个菜单(城市/类型),会出现一个下拉菜单:
菜单的内容会从云端获取,这里先看下searchAct()的逻辑。
index.js 》 line:136- 01 function searchAct(el,type){ // el 为由哪个标签出发的
- 02 var header = $api.byId('header');
- 03 var pos = $api.offset(header);
- 04 var index = 0; // 默认显示窗口组的第一个窗口
- 05 if(type === "type"){ // 如果第二个标签会触发则打开窗口组第二个窗口
- 06 index = 1;
- 07 }
- 08 if(!searchActOpened){ // 如果是第一次打开窗口组
- 09 api.openFrameGroup ({
- 10 name: 'searchAct', // 窗口组名称
- 11 rect:{x: 0, y: pos.h, w: 'auto', h: 'auto'}, // 在header下面开始显示,宽和高根据内容多少自调整
- 12 index: index, // 打开哪个窗口
- 13 frames:[{
- 14 name: 'searchActBy-city', // 城市页面
- 15 url: 'html/searchActBy-city.html',
- 16 bgColor: 'rgba(51,51,51,0.6)',
- 17 },{
- 18 name: 'searchActBy-type', // 类型页面
- 19 url: 'html/searchActBy-type.html',
- 20 bgColor: 'rgba(51,51,51,0.6)',
- 21 }]}, function(ret, err){
- 22 });
- 23 searchActOpened = true; // 标记窗口组已被打开
- 24 }else{ // 若窗口组之前被打开过,则直接显示/隐藏其中某个窗口
- 25 api.setFrameGroupIndex ({
- 26 name: 'searchAct',
- 27 index: index
- 28 });
- 29 api.setFrameGroupAttr({
- 30 name: 'searchAct',
- 31 hidden: false
- 32 });
- 33 }
- 34 var curLi = el.parentNode; // 默认是a标签触发的,我们找到它的父亲li标签
- 35 if($api.hasCls(curLi,'active')){ // 关闭下拉菜,如果第一次被点击过,这里指第二次点击动作,隐藏之
- 36 api.setFrameGroupAttr({
- 37 name: 'searchAct',
- 38 hidden: true // 先藏起来
- 39 });
- 40 }
- 41 $api.toggleCls(curLi,'active'); // 设置菜单打开/关闭的状态表示,见下图!
- var lis = $api.domAll('#header .activity li');
- var i = 0, len = lis.length;
- for(i; i<len; i++){
- var thisLi = lis[i];
- if(thisLi === curLi){
- continue;
- }else{
- if($api.hasCls(thisLi,'active')){
- $api.removeCls(thisLi,'active');
- }
- }
- }
- }
复制代码
我们这里已经将下拉菜单的基本逻辑框架写出来了:
》行04-07 :判断用户点击的是“城市”菜单还是“类型”菜单。
》行08-23 :当第一次打开菜单,同时加载两个菜单页面,根据用户选择,只显示一个。
》行16、20:定义打开的下拉菜单窗口的背景色:灰色半透明。
》行24-33 :之后再次打开菜单,直接显示之前被隐藏的窗口,减小开销嘛。
》行35-40 :隐藏下拉菜单。(单击菜单显示下拉,再单击则隐藏该下拉)
》行41 :触发/关闭 菜单时,该菜单的显示效果:
3、下拉菜单的内容
当用户触发“城市”菜单后,会打开searchActBy-city.html,其js加载事件会负责连接云端,下载“城市”菜单页面的内容:
searchAct.js- apiready = function() {
- getActFilter(); // js加载
- };
- function getActFilter(){
- var model = api.require('model'); // model模型
- var query = api.require('query');
- var cityCon = $api.byId('city-content');
- query.createQuery(function(ret, err) {
- if (ret && ret.qid) {
- var queryId = ret.qid;
- if(cityCon){
- model.findAll({
- class: "allCity", // 云端数据库,表allCity
- qid: queryId
- }, function(ret, err) {
- if (ret) {
- var content = $api.byId('city-content'); // doT模板什么的前面说过好几遍了....
- var tpl = $api.byId('city-template').text;
- var tempFn = doT.template(tpl);
- content.innerHTML = tempFn(ret);
- api.parseTapmode();
- ..............................
- }
复制代码 下载后的数据如此格式化输出:
searchActBy-city.html- <ul id="activityCity">
- <script id="city-template" type="text/x-dot-template">
- {{ for(var i=0, len=it.length; i<len; i++) {}}
- {{? !!it[i].sum }}
- <li>
- <a tapmode="active" onclick="searchActArea('{{=it[i].city}}');"> //当用户选择某个菜单项后
- <span>{{=it[i].city}}</span> <em>{{=it[i].sum}}</em>
- </a>
- </li>
- {{?}}
- {{ } }}
- </script>
- <div id="city-content"></div>
复制代码
4、选中菜单项后更header与菜单项对应数据
这里,当用户选择某个菜单项目后,我们需要更新header中的城市菜单内容,并且云端下载与该城市相关的数据:
searchAct.js
- 01 function searchActArea(cityName) {
- 02 if(!cityName){return;}
- 03
- 04 // 更新header菜单显示为当前所选城市
- 05 var that = $api.dom(event.target, 'span'); // 获取被选择菜单项
- 06 var txt = $api.text(that); // 获取文本
- 07 api.execScript({ // 跨窗口调用js函数
- 08 name: 'root', // 调用顶层窗口index.html, 所有header都在index.html定义,index.html的名称为root
- 09 script: 'changeCityTab("'+ txt +'");' // 改变内容
- 10 });
- 11
- 12 // 根据所选城市下载所需数据
- 13 api.execScript({
- 14 frameName: 'activity', // 在activity窗口执行
- 15 script: 'getDataByFilter("city", "'+ cityName +'");' // 当用户选择城市后,自动云端下载该城市相关的活动
- 16 });
- 17 }
复制代码 行05-10:更新header信息为所选菜单项(城市)内容。
行07-10、行13-16是跨窗口调用,也就是执行不同窗口的js函数。
行13-16:跨窗口调用其getDataByFilter(),获取所选城市相关数据。
Ok,到这里整个菜单的调用过程就完整了。
5、用户选择菜单项后,关闭整个下拉菜单窗口。
searchAct.js
- 01 var body = $api.dom('body');
- 02 var contains = function(parent, el) {
- 03 var mark = false;
- 04 if (el === parent) {
- 05 mark = true;
- 06 return mark;
- 07 } else {
- 08 do {
- 09 el = el.parentNode;
- 10 if (el === parent) {
- 11 mark = true;
- 12 return mark;
- 13 }
- 14 } while (el === document.body || el === document.documentElement);
- 15 return mark;
- 16 }
- 17 };
- 18 $api.addEvt(body, 'touchend', function(e) {
- 19 var main = $api.dom('#main');
- 20 var wrap = $api.dom('#wrap');
- 21 if (!contains(main, e.target) || !contains(wrap, e.target)) {
- 22 api.execScript({
- 23 name: 'root',
- 24 script: 'closeFramGroup();'
- 25 });
- 26 }
- 27 });
复制代码 这段代码的作用是:捕获下拉菜单页面的“触屏”事件,当用户选择了某个菜单项或者触发了页面其他部分(透明部分),则关闭此下拉菜单页面。
》行08-14:以冒泡的方式,逐层匹配是否在main和wrap中被触发,不过我实际调试的过程中这部分代码并没有成功冒泡至main或者wrap。选择某菜单项,只冒泡至LI标签;选择半透背景部分,则冒泡至HTML。不知其具体原因,主要是我水平菜。总之要成功执行,返回false是必须的。
其实,我们要判断当前点击是否在下拉菜单页面中,下面这样写也是可以实现的啦:
- var body = $api.dom('body');
- $api.addEvt(body, 'touchend', function(e) {
- api.execScript({
- name: 'root',
- script: 'closeFramGroup();'
- });
- });
复制代码
文章导航:
从1开始学晋城(一) —— 概览
从1开始学晋城(二) —— 首页main部分
从1开始学晋城(三) —— 蓝色样式
从1开始学晋城(四) —— 热门活动
从1开始学晋城(五) —— 下拉菜单
|