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 相关文章推荐
不错的一个日期输入 动态
Nov 06 Javascript
javascript 兼容FF的onmouseenter和onmouseleave的代码
Jul 19 Javascript
一个JavaScript处理textarea中的字符成每一行实例
Sep 22 Javascript
AngularJS + Node.js + MongoDB开发的基于高德地图位置的通讯录
Jan 02 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
Sep 27 Javascript
JS实现将数字金额转换为大写人民币汉字的方法
Aug 02 Javascript
使用BootStrap实现用户登录界面UI
Aug 10 Javascript
D3.js实现直方图的方法详解
Sep 25 Javascript
详解webpack分离css单独打包
Jun 21 Javascript
微信小程序左右滑动的实现代码
Dec 15 Javascript
Vue框架里使用Swiper的方法示例
Sep 20 Javascript
一些手写JavaScript常用的函数汇总
Apr 16 Javascript
详解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
PHP 存取 MySQL 数据库的一个例子
2006/10/09 PHP
php在window iis的莫名问题的测试方法
2013/05/14 PHP
学习php开源项目的源码指南
2014/12/21 PHP
php 一维数组的循环遍历实现代码
2017/04/10 PHP
PHP设计模式之抽象工厂模式实例分析
2019/03/25 PHP
PHP+redis实现微博的拉模型案例详解
2019/07/10 PHP
Yii使用EasyWechat实现小程序获取用户的openID的方法
2020/04/29 PHP
如何在PHP环境中使用ProtoBuf数据格式
2020/06/19 PHP
JavaScript对象之间的转换 jQuery对象和原声DOM
2011/03/07 Javascript
javascript定时器完整实例
2015/02/10 Javascript
js实现同一页面多个运动效果的方法
2015/04/10 Javascript
创建你的第一个AngularJS应用的方法
2015/06/16 Javascript
JS实现的自定义右键菜单实例二则
2015/09/01 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
jquery拖动层效果插件用法实例分析(附demo源码)
2016/04/28 Javascript
jQuery解决$符号命名冲突
2016/06/18 Javascript
javaScript中定义类或对象的五种方式总结
2016/12/04 Javascript
jQuery滑动到底部加载下一页数据的实例代码
2017/05/22 jQuery
Vue v2.4中新增的$attrs及$listeners属性使用教程
2018/01/08 Javascript
Angular整合zTree的示例代码
2018/01/24 Javascript
大转盘抽奖小程序版 转盘抽奖网页版
2020/04/16 Javascript
浅谈vue单页面中有多个echarts图表时的公用代码写法
2020/07/19 Javascript
Python正则表达式常用函数总结
2017/06/24 Python
老生常谈python中的重载
2018/11/11 Python
解决pyinstaller打包发布后的exe文件打开控制台闪退的问题
2019/06/21 Python
OpenCV哈里斯(Harris)角点检测的实现
2020/01/15 Python
VS2019+python3.7+opencv4.1+tensorflow1.13配置详解
2020/04/16 Python
有关HTML5中背景音乐的自动播放功能
2017/10/16 HTML / CSS
办公室主任职责范文
2013/11/08 职场文书
客服主管岗位职责
2013/12/13 职场文书
英文请假条
2014/04/11 职场文书
计算机应用专业自荐信
2014/07/05 职场文书
黄石寨导游词
2015/02/05 职场文书
承诺书的内容有哪些,怎么写?
2019/06/21 职场文书
redis三种高可用方式部署的实现
2021/05/11 Redis
python中出现invalid syntax报错的几种原因分析
2022/02/12 Python