Bootstrap每天必学之级联下拉菜单


Posted in Javascript onMarch 27, 2016

本文将介绍自定义的bootstrap级联下拉菜单,主要应用场合有省市级关联菜单等等,那么就先拿这个例子来讲,当然其他场景的关联菜单也同样适用。说实话,封装好一个通用的组件还是需要花费很多精力的和时间的,所谓通用,自然要考虑周全,叹!这次整理的Bootstrap关联select,里面也涉及到了很多jquery、ajax、springMVC等等知识点,可谓包罗万象!

首先,请允许我代表该自定义组件做一番小小的介绍。

“hi,你好,我叫yunm.combox.js,主人给我起的名字,其实呢,挺俗的。我主要通过为select组件增加两个自定义属性来完成相应的数据加载,数据请求使用了ajax,后端数据处理使用了springMVC(当然其他方式也可以,只需要返回对应的json数据即可),使用起来呢,就非常非常简单了!”

一、界面效果

Bootstrap每天必学之级联下拉菜单

当然了,从界面上完全看不出来一个组件封装的好坏,但至少,你感觉很简洁漂亮,那么好了,有了这层印象,你是否有兴趣继续看下去?我想答案是肯定的。

二、使用方法

①、procity.jsp

首先呢,在页面上加载yunm.combox.js(稍候介绍,至于其他的bootstrap的css和js,不在本章介绍范围内,略过),同时呢,创建两个select,具体格式见如下:

<script type="text/javascript" src="${ctx}/components/yunm/yunm.combox.js"></script>
<div class="form-group">
 <div class="row">
 <div class="col-md-6">
 <select name="province_code" class="form-control combox" ref="city_select"
 refUrl="${ctx}/procity?pro_code={value}&city_code=HSLY">
 </select>
 </div>
 <div class="col-md-6">
 <select name="city_code" id="city_select" class="form-control">
 </select>
 </div>
 </div>
</div>
<script type="text/javascript">
<!--
 $(function() {
 if ($.fn.combox) {
 $("select.combox", $p).combox();
 }
 });
//-->
</script>

·两个select组件,一个为province_code、一个为city_code。
·省级菜单上增加了两个属性。
      ref指定关联菜单为市级菜单city_select
      refUrl指定菜单获取数据的URL 
                pro_code作为获取市级数据的关键因子
                {value}呢,则为通配符,稍候在介绍组件的时候继续讲到
               city_code=HSLY,主要用于选中指定的省市菜单,诸如上文中的(河南、洛阳),如果不选中,则city_code=为空
·class=”combox” 为该省级下拉框增加jquery选择器
·页面加载完毕后执行combox组件的关键方法,下面详细介绍

②、yunm.combox.js

现在我们来看看关键的组件内容吧!

(function($) {
 var _onchange = function(event) {
 var $ref = $("#" + event.data.ref);
 if ($ref.size() == 0)
 return false;

 var refUrl = event.data.refUrl;
 var value = encodeURIComponent(event.data.$this.val());
 YUNM.debug(value);

 $.ajax({
 type : 'POST',
 dataType : "json",
 url : refUrl.replace("{value}", value),
 cache : false,
 data : {},
 success : function(response) {
 $ref.empty();

 addHtml(response, $ref);
 $ref.trigger("change").combox();
 },
 error : YUNM.ajaxError
 });

 };

 var addHtml = function(response, $this) {
 var json = YUNM.jsonEval(response);
 if (!json)
 return;

 var html = '';
 $.each(json, function(i) {
 if (json[i]) {

 html += '<option value="' + json[i].value + '"';

 if (json[i].selected) {
  html += ' selected="' + json[i].selected;
 }

 html += '">' + json[i].name + '</option>';
 }
 });

 $this.html(html);
 };

 $.extend($.fn, {
 combox : function() {

 return this.each(function(i) {
 var $this = $(this);

 var value = $this.val() || '';
 var ref = $this.attr("ref");

 var refUrl = $this.attr("refUrl") || "";
 if (refUrl) {
  refUrl = refUrl.replace("{value}", encodeURIComponent(value));
 }

 if (refUrl) {
  $.ajax({
  type : 'POST',
  dataType : "json",
  url : refUrl,
  cache : false,
  data : {},
  success : function(response) {
  addHtml(response, $this);

  if (ref && $this.attr("refUrl")) {
  $this.unbind("change", _onchange).bind("change", {
   ref : ref,
   refUrl : $this.attr("refUrl"),
   $this : $this,
  }, _onchange).trigger("change");
  }
  },
  error : YUNM.ajaxError
  });
 }

 });
 }
 });
})(jQuery);

·通过$.extend($.fn, { combox : function() {为jquery增加一个叫combox的底层(可以查询jquery帮助文档)方法。
·通过(function($){_onchange、addHtml})(jQuery);为该组件在页面初始加载时创建两个方法onchange和addHtml,至于(function($) {})(jQuery);我想你如果不了解的话,赶紧百度吧!
·先来看combox 方法 
            获取ref、refUrl,通过ajax向refUrl请求省级菜单数据,当获取成功后,通过addHtml方法将json转换后的option绑定到省级菜单select上
            然后呢,为省级菜单select绑定change事件,传递的参数为ref(市级菜单)、refUrl(市级数据获取的url)、$this(省级菜单,便于change事件获取对应选中项,如效果图中的河南)
            通过trigger方法立即执行change事件,便于获取对应的市级菜单内容。
·再来看_onchange方法,主要是点击省级菜单时触发,用于获取市级菜单列表
            refUrl,向服务端请求的URL
            value,用于获取省级菜单的选中项目,然后通过该value值获取省级对应的市级菜单
            $ref.empty();用于清空市级菜单
            通过ajax继续获取市级菜单内容,然后通过addHtml方法添加到市级菜单中。
·addHtml方法
           通过jsonEval方法对服务端传递回来的数据进行eval(eval('(' + data + ')'),如有不懂,可百度)方法处理,否则会出错。
           $.each(json, function(i) {遍历json,通过jquery创建option对象,然后加入到select中。

③、ProcityController

前端介绍完了,我们回到后端进行介绍,当然了,你也可以忽略本节,因为不是所用的关联数据都通过springMVC这种方法获取,那么先预览一下代码吧!

package com.honzh.spring.controller;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.honzh.biz.database.entity.City;
import com.honzh.biz.database.entity.Option;
import com.honzh.biz.database.entity.Provincial;
import com.honzh.common.util.JsonUtil;
import com.honzh.spring.service.CityService;
import com.honzh.spring.service.ProvincialService;

@Controller
@RequestMapping(value = "/procity")
public class ProcityController extends BaseController {
 private static Logger logger = Logger.getLogger(ProcityController.class);

 /**
 * 当传递city_code,则表明下拉框要被选中,否则不选中
 */
 @RequestMapping("")
 public void index(@RequestParam(value = "city_code", required = false) String city_code,
 @RequestParam(value = "pro_code", required = false) String pro_code, HttpServletResponse response) {
 try {
 logger.debug("获取所在地区" + city_code + ", 省" + pro_code);

 // 如果pro_code为””,则表明要获取城市菜单,否则获取市级菜单
 if (!pro_code.equals("")) {
 Integer pro_id = ProvincialService.getInstance().getByProvincialcode(pro_code).getId();
 List<City> citys = CityService.getInstance().getCitysByProvincialId(pro_id);
 List<Option> coptions = new ArrayList<Option>(citys.size());

 for (City city : citys) {
  Option coption = new Option();
  coption.setId(city.getId());
  coption.setName(city.getCname());
  coption.setValue(city.getCode());

  // 市级菜单被选中
  if (city_code != null && !city_code.equals("")) {
  if (city.getCode().equals(city_code)) {
  coption.setSelected("selected");
  }
  }
  coptions.add(coption);
 }
 renderJson(response, coptions);
 } else {
 List<Provincial> provincials = ProvincialService.getInstance().getProvincials();

 // 转换成标准的option属性(name,value,selected)
 List<Option> options = new ArrayList<Option>(provincials.size());

 // 被选中的省市
 // 则说明是展示页面,此时需要为省级菜单和市级菜单设置选择项
 if (city_code != null && !city_code.equals("")) {
  Provincial selected_provincial = ProvincialService.getInstance().getProvincialByCitycode(city_code);

  pro_code = selected_provincial.getProcode();
 } else {
  pro_code = provincials.get(0) == null ? "" : provincials.get(0).getProcode();
 }

 for (Provincial provincial : provincials) {
  Option option = new Option();
  option.setId(provincial.getId());
  option.setName(provincial.getProname());
  option.setValue(provincial.getProcode());

  if (!pro_code.equals("") && provincial.getProcode().equals(pro_code)) {
  option.setSelected("selected");
  }

  options.add(option);
 }

 renderJson(response, JsonUtil.toJson(options));
 }

 } catch (Exception e) {
 logger.error(e.getMessage());
 logger.error(e.getMessage(), e);

 renderJson(response, null);
 }
 }

}

@RequestParam(value = "city_code", required = false) String city_code,对于RequestParam注解,其实非常好用,这里就不多做解释,只是推广一下,固定个数的参数,用该注解更易于代码的维护。
ProvincialService类、CityService类就是两个单例,尽量把数据放置在内存当中,减少查询数据库的次数,稍候贴出来一个例子。
Option类就是单纯的封装前端option组件的关键属性,便于组件的通用化。
renderJson(response, JsonUtil.toJson(options));将数据json化后返回,稍候贴上详细代码。

④、ProvincialService.java

只贴出来代码例子,不做详细解释,毕竟不是本章重点。

package com.honzh.spring.service;

import java.util.ArrayList;
import java.util.List;

import com.honzh.biz.database.entity.City;
import com.honzh.biz.database.entity.Provincial;
import com.honzh.biz.database.mapper.ProvincialMapper;
import com.honzh.common.spring.SpringContextHolder;

public class ProvincialService {

 private static Object lock = new Object();
 private static ProvincialService config = null;

 private ProvincialService() {
 provincials = new ArrayList<Provincial>();

 ProvincialMapper mapper = SpringContextHolder.getBean(ProvincialMapper.class);
 provincials.addAll(mapper.getProvincials());
 }

 public static ProvincialService getInstance() {
 synchronized (lock) {
 if (null == config) {
 config = new ProvincialService();
 }
 }
 return (config);
 }

 public Provincial getByProvincialcode(String provincial_code) {
 for (Provincial provincial : provincials) {
 if (provincial.getProcode().equals(provincial_code)) {
 return provincial;
 }
 }
 return null;
 }

 private List<Provincial> provincials = null;

 public List<Provincial> getProvincials() {
 return provincials;
 }

 public Provincial getProvincialByCitycode(String city_code) {
 City city = CityService.getInstance().getCityByCode(city_code);

 for (Provincial provincial : provincials) {
 if (provincial.getId().intValue() == city.getProid().intValue()) {
 return provincial;
 }
 }
 return null;
 }

 public Provincial getProvincialByCode(String province_code) {
 for (Provincial provincial : provincials) {
 if (provincial.getProcode().equals(province_code)) {
 return provincial;
 }
 }
 return null;
 }

}

⑤、renderJson方法

/**
 * 如果出错的话,response直接返回404
 */
 protected void renderJson(HttpServletResponse response, Object responseObject) {
 PrintWriter out = null;
 try {
 if (responseObject == null) {
 response.sendError(404);
 return;
 }
 // 将实体对象转换为JSON Object转换
 String responseStr = JsonUtil.toJson(responseObject);
 response.setCharacterEncoding("UTF-8");
 response.setContentType("application/json; charset=utf-8");

 out = response.getWriter();
 out.append(responseStr);

 logger.debug("返回是:" + responseStr);
 } catch (IOException e) {
 logger.error(e.getMessage());
 logger.error(e.getMessage(), e);
 } finally {
 if (out != null) {
 out.close();
 }
 }
 }

如果大家还想深入学习,可以点击jQuery级联菜单特效汇总、Javascript级联菜单特效汇总进行学习。

如果大家还想深入学习Bootstrap,可以点击这里进行学习,再为大家附两个精彩的专题:Bootstrap学习教程 Bootstrap实战教程

本文系列教程整理到:Bootstrap基础教程 专题中,欢迎点击学习。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
jQuery 使用手册(二)
Sep 23 Javascript
基于jquery的实现简单的表格中增加或删除下一行
Aug 01 Javascript
基于Jquery的淡入淡出的特效基础练习
Dec 13 Javascript
容易造成JavaScript内存泄露几个方面
Sep 04 Javascript
基于jQuery实现的QQ表情插件
Aug 25 Javascript
Jquery实现纵向横向菜单
Jan 24 Javascript
JavaScript实现替换字符串中最后一个字符的方法
Mar 07 Javascript
微信小程序多列表渲染数据开关互不影响的实现
Jun 05 Javascript
ES2020系列之空值合并运算符 '??'
Jul 22 Javascript
vue $mount 和 el的区别说明
Sep 11 Javascript
解决vue scoped html样式无效的问题
Oct 24 Javascript
Vue项目利用axios请求接口下载excel
Nov 17 Vue.js
详解javascript跨浏览器事件处理程序
Mar 27 #Javascript
js事件处理程序跨浏览器解决方案
Mar 27 #Javascript
基于javascript实现九九乘法表
Mar 27 #Javascript
深入浅析JavaScript中的作用域和上下文
Mar 26 #Javascript
JS中改变this指向的方法(call和apply、bind)
Mar 26 #Javascript
浏览器复制插件zeroclipboard使用指南
Mar 26 #Javascript
jquery中validate与form插件提交的方式小结
Mar 26 #Javascript
You might like
DC漫画《蝙蝠侠和猫女》图透 猫女怀孕老爷当爹
2020/04/09 欧美动漫
List Information About the Binary Files Used by an Application
2007/06/18 Javascript
深入理解JavaScript系列(12) 变量对象(Variable Object)
2012/01/16 Javascript
自写的jQuery异步加载数据添加事件
2014/05/15 Javascript
通过JS来动态的修改url,实现对url的增删查改
2014/09/01 Javascript
jquery判断单选按钮radio是否选中的方法
2015/05/05 Javascript
JavaScript对HTML DOM使用EventListener进行操作
2015/10/21 Javascript
jQuery实现滑动页面固定顶部显示(可根据显示位置消失与替换)
2015/10/28 Javascript
JavaScript几种数组去掉重复值的方法推荐
2016/04/12 Javascript
一次$.getJSON不执行的简单记录
2016/07/19 Javascript
AngularJS 模块化详解及实例代码
2016/09/14 Javascript
Javascript 实现微信分享(QQ、朋友圈、分享给朋友)
2016/10/21 Javascript
Node.js+Express+MySql实现用户登录注册功能
2017/07/10 Javascript
详谈for循环里面的break和continue语句
2017/07/20 Javascript
纯JS实现五子棋游戏
2020/05/28 Javascript
python实现目录树生成示例
2014/03/28 Python
PyQt5打开文件对话框QFileDialog实例代码
2018/02/07 Python
解决pyecharts在jupyter notebook中使用报错问题
2020/04/23 Python
利用Python绘制有趣的万圣节南瓜怪效果
2019/10/31 Python
Python动态导入模块和反射机制详解
2020/02/18 Python
Python爬虫简单运用爬取代理IP的实现
2020/12/01 Python
各大浏览器 CSS3 和 HTML5 兼容速查表 图文
2010/04/01 HTML / CSS
ghd官网:英国ghd直发器品牌
2018/05/04 全球购物
中文专业毕业生自荐信
2013/10/28 职场文书
业务助理岗位职责
2013/11/18 职场文书
初中生个人学习的自我评价
2013/12/04 职场文书
养殖行业的创业计划书
2014/01/05 职场文书
企业负责人任命书
2014/06/05 职场文书
房地产营销活动策划方案
2014/09/15 职场文书
检察院院长群众路线教育实践活动个人整改措施
2014/10/04 职场文书
2014年库房工作总结
2014/11/26 职场文书
齐云山导游词
2015/02/06 职场文书
公司出差管理制度范本
2015/08/05 职场文书
Python基础教程,Python入门教程(超详细)
2021/06/24 Python
Win11跳过联网界面创建本地管理账户的3种方法
2022/04/20 数码科技
IDEA中sout快捷键无效问题的解决方法
2022/07/23 Java/Android