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图片平滑连续滚动插件
Apr 27 Javascript
javascript 获取元素位置的快速方法 getBoundingClientRect()
Nov 26 Javascript
JavaScript DOM 学习第二章 编辑文本
Feb 19 Javascript
jQuery 源码分析笔记(6) jQuery.data
Jun 08 Javascript
Javascript计算时间差的函数分享
Jul 04 Javascript
非常有用的40款jQuery 插件推荐(系列二)
Dec 25 Javascript
javascript下拉框选项单击事件的例子分享
Mar 04 Javascript
jQuery插件编写步骤详解
Jun 03 Javascript
AngularJS基础 ng-class-odd 指令示例
Aug 01 Javascript
基于bootstrap实现收缩导航条
Mar 17 Javascript
AngularJS之ionic 框架下实现 Localstorage本地存储
Apr 22 Javascript
详解html-webpack-plugin插件(用法总结)
Sep 12 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 curl基本操作详解
2013/07/23 PHP
php获取汉字首字母的函数
2013/11/07 PHP
php初始化对象和析构函数的简单实例
2014/03/11 PHP
php使用正则表达式获取图片url的方法
2015/01/16 PHP
Extjs学习笔记之四 工具栏和菜单
2010/01/07 Javascript
JS 有名函数表达式全面解析
2010/03/19 Javascript
jquery 多级下拉菜单核心代码
2010/05/21 Javascript
JavaScript 5 新增 Array 方法实现介绍
2012/02/06 Javascript
JS实现关键字搜索时的相关下拉字段效果
2014/08/05 Javascript
javascript设置连续两次点击按钮时间间隔的方法
2014/10/28 Javascript
基于jquery的手风琴图片展示效果实现方法
2014/12/16 Javascript
跟我学习javascript的垃圾回收机制与内存管理
2015/11/23 Javascript
深入学习jQuery Validate表单验证
2016/01/18 Javascript
Javascript操作表单实例讲解(下)
2016/06/20 Javascript
jQuery实现边框动态效果的实例代码
2016/09/23 Javascript
js精确的加减乘除实例
2017/11/14 Javascript
浅析node Async异步处理模块用例分析及常用方法介绍
2017/11/17 Javascript
VUE 实现滚动监听 导航栏置顶的方法
2018/09/11 Javascript
[02:39]DOTA2国际邀请赛助威团西雅图第一天
2013/08/08 DOTA
从零学Python之hello world
2014/05/21 Python
python异步编程 使用yield from过程解析
2019/09/25 Python
Python vtk读取并显示dicom文件示例
2020/01/13 Python
Python中的sys.stdout.write实现打印刷新功能
2020/02/21 Python
关于Kotlin中SAM转换的那些事
2020/09/15 Python
Myprotein加拿大官网:欧洲第一的运动营养品牌
2018/01/06 全球购物
仓库班组长岗位职责
2013/12/12 职场文书
服装促销活动方案
2014/02/23 职场文书
社团活动总结报告
2014/06/27 职场文书
义务教育学校标准化建设汇报材料
2014/08/16 职场文书
安全在我心中演讲稿
2014/09/01 职场文书
电子商务实训报告总结
2014/11/05 职场文书
2014年组织部工作总结
2014/11/14 职场文书
银行稽核岗位职责
2015/04/13 职场文书
杨善洲电影观后感
2015/06/04 职场文书
幼儿园六一主持词
2015/06/30 职场文书
2015年新教师个人工作总结
2015/10/14 职场文书