apicloud apicloud

注册
查看: 15049|回复: 16

[新手教程] 从1开始学晋城(五) —— 下拉菜单

主题:
28
帖子:
88
云币:
757

[新手教程] 从1开始学晋城(五) —— 下拉菜单

15049 16 | 发表于 2014-12-18 15:36:00 |阅读模式 | |
本帖最后由 老邮局 于 2015-7-23 20:50 编辑

》》更多视频教程请关注 www.9y.cm《《

我们来看"活动"页面,布局没什么好说的,我们主要看下header部分的两个下拉菜单:

下拉菜单其实是一个新的窗口,背景色为灰色半透,上半部分是不透的ul和li标签。

1、header默认样式
2、下拉菜单的逻辑与实现
3、下拉菜单的数据
4、更新header与对应下拉菜单项数据
5、选择后关闭下拉菜单


1、header默认样式
在第一篇我们说过了,所有header都在index.html定义好了,默认只显示一个,当底部导航栏切换后,header会对应的切换显示。
index.html
  1. 28  <div class="activity header">
  2. 29          <h1>玩转晋城</h1>
  3. 30          <ul class="submenu">
  4. 31          <li><a tapmode="" onclick="searchAct(this,'city')" class="city"><i></i><span>全城</span></a></li>
  5. 32          <li><a tapmode="" onclick="searchAct(this,'type')" class="hot"><i></i><span>全部类型</span></a></li>
  6. 33          </ul>
  7. 34  </div>
复制代码

2、
下拉菜单的逻辑与实现
此时当用户点击任何一个菜单(城市/类型),会出现一个下拉菜单:


菜单的内容会从云端获取,这里先看下searchAct()的逻辑。
index.js  》 line:136
  1. 01  function searchAct(el,type){                                              // el 为由哪个标签出发的
  2. 02          var header = $api.byId('header');
  3. 03          var pos = $api.offset(header);

  4. 04          var index = 0;                                                             // 默认显示窗口组的第一个窗口
  5. 05          if(type === "type"){                                                  // 如果第二个标签会触发则打开窗口组第二个窗口   
  6. 06                  index = 1;
  7. 07          }
  8. 08          if(!searchActOpened){                                              // 如果是第一次打开窗口组
  9. 09                  api.openFrameGroup ({
  10. 10                          name: 'searchAct',                                    // 窗口组名称
  11. 11                          rect:{x: 0, y: pos.h, w: 'auto', h: 'auto'},     // 在header下面开始显示,宽和高根据内容多少自调整
  12. 12                          index: index,                                             // 打开哪个窗口
  13. 13                          frames:[{
  14. 14                                              name: 'searchActBy-city',    // 城市页面
  15. 15                                              url: 'html/searchActBy-city.html',
  16. 16                                              bgColor: 'rgba(51,51,51,0.6)',
  17. 17                                      },{
  18. 18                                              name: 'searchActBy-type',   // 类型页面
  19. 19                                              url: 'html/searchActBy-type.html',
  20. 20                                              bgColor: 'rgba(51,51,51,0.6)',
  21. 21                           }]}, function(ret, err){
  22. 22                 });
  23. 23                   searchActOpened = true;                               // 标记窗口组已被打开
  24. 24        }else{                                                                        // 若窗口组之前被打开过,则直接显示/隐藏其中某个窗口
  25. 25                   api.setFrameGroupIndex ({
  26. 26                           name: 'searchAct',
  27. 27                           index: index
  28. 28                   });
  29. 29                   api.setFrameGroupAttr({
  30. 30                           name: 'searchAct',
  31. 31                           hidden: false
  32. 32                   });
  33. 33          }

  34. 34          var curLi = el.parentNode;                                     // 默认是a标签触发的,我们找到它的父亲li标签
  35. 35          if($api.hasCls(curLi,'active')){                                 // 关闭下拉菜,如果第一次被点击过,这里指第二次点击动作,隐藏之
  36. 36                  api.setFrameGroupAttr({
  37. 37                          name: 'searchAct',
  38. 38                          hidden: true                                            // 先藏起来
  39. 39                  });
  40. 40           }
  41. 41          $api.toggleCls(curLi,'active');                                 // 设置菜单打开/关闭的状态表示,见下图!

  42.         var lis = $api.domAll('#header .activity li');
  43.         var i = 0, len = lis.length;

  44.         for(i; i<len; i++){
  45.                 var thisLi = lis[i];
  46.                 if(thisLi === curLi){
  47.                         continue;
  48.                 }else{
  49.                         if($api.hasCls(thisLi,'active')){
  50.                        $api.removeCls(thisLi,'active');
  51.                 }
  52.           }
  53.      }
  54. }
复制代码

我们这里已经将下拉菜单的基本逻辑框架写出来了:

》行04-07  :判断用户点击的是“城市”菜单还是“类型”菜单。

》行08-23  :当第一次打开菜单,同时加载两个菜单页面,根据用户选择,只显示一个。
》行16、20:定义打开的下拉菜单窗口的背景色:灰色半透明。
》行24-33 :之后再次打开菜单,直接显示之前被隐藏的窗口,减小开销嘛。
》行35-40 :隐藏下拉菜单。(单击菜单显示下拉,再单击则隐藏该下拉)

》行41        :触发/关闭 菜单时,该菜单的显示效果:

3、下拉菜单的内容
当用户触发“城市”菜单后,会打开searchActBy-city.html,其js加载事件会负责连接云端,下载“城市”菜单页面的内容:

searchAct.js
  1. apiready = function() {
  2.         getActFilter();              // js加载
  3. };
  4. function getActFilter(){
  5.         var model = api.require('model');            // model模型
  6.         var query = api.require('query');
  7.         var cityCon = $api.byId('city-content');

  8.         query.createQuery(function(ret, err) {

  9.         if (ret && ret.qid) {
  10.                 var queryId = ret.qid;

  11.                 if(cityCon){
  12.                         model.findAll({
  13.                         class: "allCity",             // 云端数据库,表allCity
  14.                         qid: queryId
  15.         }, function(ret, err) {
  16.                 if (ret) {

  17.                         var content = $api.byId('city-content');          // doT模板什么的前面说过好几遍了....
  18.                         var tpl = $api.byId('city-template').text;
  19.                         var tempFn = doT.template(tpl);
  20.                         content.innerHTML = tempFn(ret);

  21.                         api.parseTapmode();
  22.             ..............................
  23. }
复制代码
下载后的数据如此格式化输出:
searchActBy-city.html
  1. <ul id="activityCity">
  2.         <script id="city-template" type="text/x-dot-template">
  3.                 {{ for(var i=0, len=it.length; i<len; i++) {}}
  4.                         {{? !!it[i].sum }}
  5.                                 <li>
  6.                                         <a tapmode="active" onclick="searchActArea('{{=it[i].city}}');">          //当用户选择某个菜单项后
  7.                                                 <span>{{=it[i].city}}</span> <em>{{=it[i].sum}}</em>
  8.                                         </a>
  9.                                 </li>
  10.                         {{?}}
  11.                 {{ } }}
  12.         </script>
  13.         <div id="city-content"></div>
复制代码

4、选中菜单项后更header与菜单项对应数据
这里,当用户选择某个菜单项目后,我们需要更新header中的城市菜单内容,并且云端下载与该城市相关的数据:

searchAct.js
  1. 01  function searchActArea(cityName) {
  2. 02        if(!cityName){return;}
  3. 03
  4. 04        // 更新header菜单显示为当前所选城市
  5. 05        var that = $api.dom(event.target, 'span');     // 获取被选择菜单项
  6. 06        var txt = $api.text(that);                                  // 获取文本
  7. 07        api.execScript({                                                // 跨窗口调用js函数
  8. 08                name: 'root',                                            // 调用顶层窗口index.html, 所有header都在index.html定义,index.html的名称为root
  9. 09                script: 'changeCityTab("'+ txt +'");'        // 改变内容
  10. 10        });
  11. 11
  12. 12        // 根据所选城市下载所需数据
  13. 13        api.execScript({
  14. 14                frameName: 'activity',                             // 在activity窗口执行
  15. 15                script: 'getDataByFilter("city", "'+ cityName +'");'   // 当用户选择城市后,自动云端下载该城市相关的活动
  16. 16       });
  17. 17  }
复制代码
行05-10:更新header信息为所选菜单项(城市)内容。
行07-10行13-16是跨窗口调用,也就是执行不同窗口的js函数。

行13-16:跨窗口调用其getDataByFilter(),获取所选城市相关数据。
Ok,到这里整个菜单的调用过程就完整了。


5、用户选择菜单项后,关闭整个下拉菜单窗口。
searchAct.js
  1. 01  var body = $api.dom('body');
  2. 02  var contains = function(parent, el) {
  3. 03          var mark = false;
  4. 04          if (el === parent) {
  5. 05                  mark = true;
  6. 06                  return mark;
  7. 07          } else {
  8. 08                  do {
  9. 09                           el = el.parentNode;
  10. 10                          if (el === parent) {
  11. 11                                  mark = true;
  12. 12                                  return mark;
  13. 13                          }
  14. 14                  } while (el === document.body || el === document.documentElement);
  15. 15                   return mark;
  16. 16           }
  17. 17  };
  18. 18  $api.addEvt(body, 'touchend', function(e) {
  19. 19          var main = $api.dom('#main');
  20. 20          var wrap = $api.dom('#wrap');
  21. 21          if (!contains(main, e.target) || !contains(wrap, e.target)) {
  22. 22                  api.execScript({
  23. 23                          name: 'root',
  24. 24                          script: 'closeFramGroup();'
  25. 25                  });
  26. 26          }
  27. 27  });
复制代码
这段代码的作用是:捕获下拉菜单页面的“触屏”事件,当用户选择了某个菜单项或者触发了页面其他部分(透明部分),则关闭此下拉菜单页面。
》行08-14:以冒泡的方式,逐层匹配是否在main和wrap中被触发,不过我实际调试的过程中这部分代码并没有成功冒泡至main或者wrap。选择某菜单项,只冒泡至LI标签;选择半透背景部分,则冒泡至HTML。不知其具体原因,主要是我水平菜。总之要成功执行,返回false是必须的。

其实,我们要判断当前点击是否在下拉菜单页面中,下面这样写也是可以实现的啦:
  1. var body = $api.dom('body');
  2. $api.addEvt(body, 'touchend', function(e) {
  3.         api.execScript({
  4.                 name: 'root',
  5.                 script: 'closeFramGroup();'
  6.         });
  7. });
复制代码




文章导航:
从1开始学晋城(一) —— 概览
从1开始学晋城(二) —— 首页main部分
从1开始学晋城(三) —— 蓝色样式

从1开始学晋城(四) —— 热门活动
从1开始学晋城(五) —— 下拉菜单


本帖子中包含更多资源    您需要 登录 才可以下载或查看,没有帐号?立即注册

1

查看全部评分

驾校小白

UID:17088

主题:
1
帖子:
14
云币:
54
发表于 2014-12-19 10:23:31 |

主题:
6
帖子:
288
云币:
3082

版主勋章端午节

发表于 2014-12-19 17:12:04 |
我去,这次的内容好多呀,支持

主题:
28
帖子:
521
云币:
85900011

APICloud粉丝端午节

发表于 2014-12-19 17:35:22 |

主题:
0
帖子:
2
云币:
10
发表于 2014-12-19 21:51:07 |
学习了

主题:
0
帖子:
10
云币:
19
发表于 2014-12-20 22:08:48 |
哈哈  好东西啊 就差这个了

新手上路

UID:103623

主题:
1
帖子:
59
云币:
219

一周年APICloud粉丝

发表于 2015-6-20 22:23:56 |
学习一下啊!

驾校小白

UID:81978

主题:
9
帖子:
28
云币:
256
发表于 2015-7-4 16:27:04 |
刚好学习,支持楼主~

主题:
8
帖子:
363
云币:
2726

APICloud粉丝

发表于 2015-7-8 18:40:51 |
好东西,赞一下

新手上路

UID:100919

主题:
0
帖子:
53
云币:
555
发表于 2015-7-14 16:53:07 |
写的不错,很详细了
12下一页
您需要登录后才可以回帖 登录 | 立即注册

快速回复 返回顶部 返回列表