zhiqim_dropdown.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*
  2. * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。
  3. *
  4. * 指定登记&发行网站: https://www.zhiqim.com/ 欢迎加盟知启蒙,[编程有你,知启蒙一路随行]。
  5. *
  6. * 本文采用《知启蒙登记发行许可证》,除非符合许可证,否则不可使用该文件!
  7. * 1、您可以免费使用、修改、合并、出版发行和分发,再授权软件、软件副本及衍生软件;
  8. * 2、您用于商业用途时,必须在原作者指定的登记网站,按原作者要求进行登记;
  9. * 3、您在使用、修改、合并、出版发行和分发时,必须包含版权声明、许可声明,及保留原作者的著作权、商标和专利等知识产权;
  10. * 4、您在互联网、移动互联网等大众网络下发行和分发再授权软件、软件副本及衍生软件时,必须在原作者指定的发行网站进行发行和分发;
  11. * 5、您可以在以下链接获取一个完整的许可证副本。
  12. *
  13. * 许可证链接:http://zhiqim.org/licenses/zhiqim_register_publish_license.htm
  14. *
  15. * 除非法律需要或书面同意,软件由原始码方式提供,无任何明示或暗示的保证和条件。详见完整许可证的权限和限制。
  16. */
  17. +(function(Z)
  18. {//BEGIN
  19. // @version v1.1.0 @author zouzhigang 2015-11-12 新建与整理
  20. /************************************************************************/
  21. //下拉列表,包括属性和方法
  22. /************************************************************************/
  23. // 1、配置方式,必须包括data-role="dropdown"表示是下拉列表
  24. // 2、可选属性data-onchange表示修改的函数,类似于select
  25. // 2、可选属性data-options值为样式格式event:click;width:160px;item-width:160px;
  26. // 3、支持的参数值
  27. // 1)event 表示该窗口的触发方式,click|hover,默认click
  28. // 2)width 表示下拉列表宽度,默认等于elem的宽度,类似于select
  29. // 3)height 表示下拉列表高度,默认200px时会出现滚动条,可以通过设计该值进行修改
  30. // 4)item-width 表示下拉项的宽度,默认等于width,一行一个类似于select,如果设置成width/2,则表示一行两个
  31. // 4、举例如下:
  32. // <div class="z-dropdown" data-role="z-dropdown" data-options="" data-onchange="onChangeProvice" style="width:160px;">
  33. // <span class="z-default" id="proviceId" value="2">湖南省</span><span class="z-float-right"><i class="z-arrow"></i></span>
  34. // <ul class="z-list">
  35. // <span value="1">广东省</span>
  36. // <span value="2" selected>湖南省</span>
  37. // <span value="3">广西壮族自治区</span>
  38. // <span value="3">江西省</span>
  39. // </ul>
  40. // </div>
  41. /************************************************************************/
  42. /********************************************/
  43. //提示类定义,包括属性和方法
  44. /********************************************/
  45. Z.Dropdown = Z.Class.newInstance();
  46. Z.Dropdown.prototype =
  47. {
  48. defaults :
  49. {//缺省值
  50. elem: null,
  51. hasSelected: true//firefox第一次focus不会show
  52. },
  53. init: function()
  54. {//初始化
  55. this.$elem = Z.$elem(this.elem, "Z.Dropdown");
  56. this.$default = this.$elem.find(".z-default");
  57. this.$input = this.$default.children('input');
  58. this.$arrow = this.$elem.find("i.z-float-right.z-font");
  59. this.$list = this.$elem.find(".z-list");
  60. this.$spans = this.$list.find("span");
  61. if (this.$list.length == 0)
  62. return;
  63. //获取配置的属性
  64. var options = Z.AR.toObject(this.$elem.attr("data-options"), ";");
  65. var expression = this.$elem.attr("data-onchange");
  66. if (Z.T.isString(expression))
  67. this.onchange = Z.evals(expression);
  68. //初始化列表高度
  69. this.event = options && options.event || "click";
  70. this.stop = options && options.stop || "false";
  71. var maxHeight = Z.S.prefixNum(options && options.maxHeight || "200");
  72. this.$list.css("maxHeight", maxHeight);
  73. //初始化条目宽高
  74. this.width = options && options.width || this.$elem.offsetWidth();
  75. var itemWidth = Z.S.prefixNum(options && options.itemWidth || this.width);
  76. var itemHeight = Z.S.prefixNum(options && options.itemHeight || "30");
  77. this.$spans.css({width: itemWidth, height: itemHeight, lineHeight: itemHeight-6});
  78. //把条目的高度缓存起来
  79. this.$list.show();
  80. this.itemHeight = this.$spans.offsetHeight();
  81. this.$list.hide();
  82. //初始化成功
  83. this.status = 0;
  84. //初始化注册移入移出事件,鼠标在元素和提示上显示
  85. this.$spans.click(this.select, this);
  86. if (this.event == "click")
  87. {
  88. this.$elem.click(this.show, this);
  89. Z(document).click(this.hide, this);
  90. }
  91. else
  92. {
  93. this.$elem.mouseover(this.show, this);
  94. this.$elem.mouseout(this.hide, this);
  95. this.$list.mouseover(this.show, this);
  96. this.$list.mouseout(this.hide, this);
  97. }
  98. if (this.$input.length > 0)
  99. {
  100. this.$input.on('focus',this.focus, this);
  101. this.$input.on('input',this.show, this);
  102. this.$input.on('blur',this.blur, this);
  103. }
  104. },
  105. focus: function(e)
  106. {
  107. this.$input.attr("placeholder", this.$input.attr("data-value"));
  108. this.$input.val("");
  109. if (Z.B.firefox && this.hasSelected)
  110. {//firefox第一次focus不会show
  111. this.hasSelected = false;
  112. this.show(e);
  113. }
  114. },
  115. show: function(e)
  116. {
  117. if (this.stop == "true")
  118. {//默认为false,即向上冒泡,以支持多个下拉列表时相互关闭,当父节点有click事件时,可设置为true,不冒泡
  119. Z.E.stop(e);
  120. }
  121. var target = Z.E.target(e);
  122. if (this.event == "click" && this.status == 1 && !(target instanceof HTMLInputElement))
  123. {//第二次点击表示关闭,如果input下点击不处理,箭头处理
  124. this.close();
  125. return;
  126. }
  127. //把列表恢复为初始模式
  128. this.status = 1;
  129. this.$elem.addClass("z-active");
  130. this.$arrow.removeClass("z-arrowhead-down").addClass("z-arrowhead-up");
  131. if (this.$input.length > 0)
  132. {
  133. this.$list.removeClass("z-none").css("height", "auto");
  134. this.$spans.removeClass("zi-hide");
  135. if (target instanceof HTMLInputElement)
  136. {//如果是从input点击的执行搜索,箭头不执行,显示全部
  137. this.search();
  138. }
  139. }
  140. //定义到元素的下方
  141. var top = this.$elem.offsetHeight() - 2;//上下边框2px
  142. var left = -1;//左边框1px
  143. this.$list.inBlock().css({top: top, left: left, width: this.width});
  144. var listRect = this.$list[0].getBoundingClientRect();
  145. var topBody = listRect.top;
  146. var height = listRect.height;
  147. if (topBody > height && Z.D.clientHeight() - topBody - height < 0)
  148. {//如果顶部够高,底部不够高时,则向上弹出
  149. top = top - height - this.$elem.offsetHeight() + 2;
  150. this.$list.css("top", top);
  151. }
  152. },
  153. hide:function(e)
  154. {
  155. if (this.stop == "true")
  156. {//默认为false,即向上冒泡,以支持多个下拉列表时相互关闭,当父节点有click事件时,可设置为true,不冒泡
  157. Z.E.stop(e);
  158. }
  159. //鼠标在元素和提示中间不关闭
  160. var x = Z.E.clientX(e);
  161. var y = Z.E.clientY(e);
  162. var tx = this.$elem.clientX();
  163. var ty = this.$elem.clientY();
  164. var tw = this.$elem.offsetWidth();
  165. var th = this.$elem.offsetHeight();
  166. if (x > tx && x < (tx+tw) && y > ty && y < (ty+th))
  167. return;
  168. this.close();
  169. },
  170. search: function()
  171. {
  172. var val = this.$input.val();
  173. this.$spans.addClass("zi-hide");
  174. this.$list.removeClass("z-none");
  175. this.$spans.each(function(elem)
  176. {//检索字符串是否存在
  177. if(Z(elem).text().indexOf(val) > -1)
  178. Z(elem).removeClass("zi-hide");
  179. });
  180. var num = this.$spans.length - this.$list.find(".zi-hide").length;
  181. this.$list.css("height", this.itemHeight * num + 2);
  182. //如果不存在匹配对象,显示提示
  183. if (this.$list.find(".zi-hide").length == this.$spans.length)
  184. {
  185. this.$list.addClass('z-none').css("height", this.itemHeight);
  186. }
  187. },
  188. blur: function(e)
  189. {//输入框退出时,恢复输入框
  190. this.$input.val(this.$input.attr("data-value"));
  191. },
  192. setValue: function(value)
  193. {
  194. if (Z.V.isEmpty(value))
  195. return;
  196. var $selected;
  197. this.$spans.each(function(item)
  198. {
  199. var $span = Z(item);
  200. if (value == $span.attr("value")){
  201. $selected = $span;
  202. return true;
  203. }
  204. });
  205. if ($selected)
  206. {//找到
  207. this.selected($selected);
  208. //设置值时,需要检查是否是z-select,是同步到select中
  209. var selectId = this.$elem.attr("data-id");
  210. if (selectId)
  211. {
  212. var sel = Z.D.id(selectId);
  213. if (sel){
  214. Z.Select.click($selected[0], sel);
  215. }
  216. }
  217. }
  218. },
  219. select: function(e)
  220. {
  221. Z.E.stop(e);
  222. this.selected(Z(Z.E.target(e)));
  223. },
  224. selected: function($selected)
  225. {
  226. this.$spans.each(function(item)
  227. {//先清理选中
  228. Z(item).removeClass("z-selected").removeAttr("selected");
  229. });
  230. $selected.addClass("z-selected");
  231. if (this.$input.length > 0)
  232. {
  233. this.$input.attr('data-value', $selected.text()).val($selected.text());
  234. }
  235. else
  236. {
  237. this.$default.val($selected.val());
  238. this.$default.text($selected.text());
  239. }
  240. this.hasSelected = true;
  241. this.close();
  242. if (this.onchange)
  243. this.onchange($selected.val());
  244. },
  245. close: function()
  246. {
  247. this.status = 0;
  248. this.$elem.removeClass("z-active");
  249. this.$arrow.removeClass("z-arrowhead-up").addClass("z-arrowhead-down");
  250. this.$list.hide();
  251. },
  252. remove: function()
  253. {
  254. this.$elem.remove();
  255. }
  256. };
  257. /********************************************/
  258. //刷新静态函数和第一次加载
  259. /********************************************/
  260. Z.Dropdown.load = function(target)
  261. {//加载
  262. Z.$selector("[data-role=z-dropdown]", target).each(function(elem){
  263. new Z.Dropdown({elem: elem});
  264. });
  265. };
  266. Z.onload(Z.Dropdown.load);
  267. /********************************************/
  268. //选择框定义和加载转换成下拉列表对象
  269. /********************************************/
  270. Z.Select = function(e)
  271. {
  272. Z.E.stop(e);
  273. Z.Select.click(Z.E.current(e), this);
  274. }
  275. Z.Select.click = function(target, elem)
  276. {
  277. var index = Z(target).attr("index");
  278. var oldIndex = elem.selectedIndex;
  279. for (var i=0;i<elem.length;i++)
  280. {//修改选中
  281. elem.options[i].selected = (index == i);
  282. }
  283. if (elem.onchange && oldIndex != index){
  284. elem.onchange();
  285. }
  286. if (elem.onblur){
  287. elem.onblur();
  288. }
  289. };
  290. Z.Select.get = function(id)
  291. {
  292. var $role = Z("[data-id="+id+"]");
  293. if ($role.length == 0)
  294. return null;
  295. return new Z.Dropdown({elem: $role[0]});
  296. };
  297. Z.Select.load = function(target)
  298. {
  299. Z.$selector(".z-role-select", target).each(function(elem)
  300. {//先删除目标范围内的下拉列表
  301. new Z.Dropdown({elem: elem}).remove();
  302. });
  303. Z.$selector("select[data-role=z-select],select[data-role=z-select-search]", target).each(function(elem)
  304. {//再加载目标范围内的数据
  305. var random = "Z_Select_"+Z.random(10);
  306. var $elem = Z(elem).hidden().attr("data-id", random);
  307. var isSearch = $elem.attr("data-role") == "z-select-search";
  308. var width = $elem.offsetWidth();
  309. var height = $elem.offsetHeight();
  310. var $cover = Z.$cover($elem);
  311. var id = Z.S.trim($elem.attr("id"));
  312. var name = Z.S.trim($elem.attr("name"))
  313. var options = "event:click;" + Z.S.trim($elem.attr("data-options"));
  314. var classes = Z.S.trim($elem.attr("data-class"));
  315. var type = $elem.attr('data-type');
  316. var dropdown = '<div class="z-dropdown z-role-select">'
  317. + '<span class="z-default z-text-clip" value=""></span><i class="z-float-right z-font z-arrowhead-down"></i>'
  318. + '<ul class="z-list"></ul>'
  319. + '</div>';
  320. var $role = Z(dropdown).appendTo($cover)
  321. .css({position: "absolute", left: 0, top: 0, width: width, height: height})
  322. .cssMaybe("padding-left", $elem.css("paddingLeft"))
  323. .cssMaybe("padding-right", $elem.css("paddingRight"))
  324. .cssMaybe("padding-top", $elem.css("paddingTop"))
  325. .cssMaybe("padding-bottom", $elem.css("paddingBottom"))
  326. .addClass(classes)
  327. .attr("id", random).attr("data-id", id).attr("data-name", name)
  328. .attr("data-options", options);
  329. //如果是模糊查找选择框
  330. if(isSearch)
  331. {
  332. $role.find(".z-default").append('<input type="text" class="zi-bd-none zi-w100p zi-h100p" placeholder="请选择" data-value="" value="">');
  333. }
  334. var hasSelected = null;
  335. for (var i=0;i<elem.length;i++)
  336. {
  337. var option = elem.options[i];
  338. var span = '<span index="'+i+'" value="'+option.value+'"';
  339. if (option.selected){
  340. hasSelected = option;
  341. span += " selected"
  342. }
  343. span += '>'+(Z.V.isEmpty(option.text)?"&nbsp;":option.text)+'</span>';
  344. var $span = Z(span);
  345. //如果有配置点击事件,则设置
  346. if (option["onclick"]){
  347. $span.attr("onclick", option["onclick"]);
  348. }
  349. //再增加单击修改到select去的事件
  350. $span.click(Z.Select, elem);
  351. $role.find('.z-list').append($span);
  352. }
  353. //如果未选中取第一个
  354. if (hasSelected == null){
  355. hasSelected = (elem.length>0)?elem.options[0]:null;
  356. }
  357. if (hasSelected)
  358. {
  359. if (isSearch){
  360. $role.find('.z-default > input').attr("data-value", hasSelected.text).val(hasSelected.text);
  361. }
  362. else{
  363. $role.find('.z-default').attr("value", hasSelected.value).text(hasSelected.text);
  364. }
  365. }
  366. //转成下拉列表
  367. new Z.Dropdown({elem: $role[0]});
  368. });
  369. };
  370. Z.onload(Z.Select.load);
  371. //END
  372. })(zhiqim);