mui.indexedlist.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * IndexedList
  3. * 类似联系人应用中的联系人列表,可以按首字母分组
  4. * 右侧的字母定位工具条,可以快速定位列表位置
  5. * varstion 1.0.0
  6. * by Houfeng
  7. * Houfeng@DCloud.io
  8. **/
  9. (function($, window, document) {
  10. var classSelector = function(name) {
  11. return '.' + $.className(name);
  12. }
  13. var IndexedList = $.IndexedList = $.Class.extend({
  14. /**
  15. * 通过 element 和 options 构造 IndexedList 实例
  16. **/
  17. init: function(holder, options) {
  18. var self = this;
  19. self.options = options || {};
  20. self.box = holder;
  21. if (!self.box) {
  22. throw "实例 IndexedList 时需要指定 element";
  23. }
  24. self.createDom();
  25. self.findElements();
  26. self.caleLayout();
  27. self.bindEvent();
  28. },
  29. createDom: function() {
  30. var self = this;
  31. self.el = self.el || {};
  32. //styleForSearch 用于搜索,此方式能在数据较多时获取很好的性能
  33. self.el.styleForSearch = document.createElement('style');
  34. (document.head || document.body).appendChild(self.el.styleForSearch);
  35. },
  36. findElements: function() {
  37. var self = this;
  38. self.el = self.el || {};
  39. self.el.search = self.box.querySelector(classSelector('indexed-list-search'));
  40. self.el.searchInput = self.box.querySelector(classSelector('indexed-list-search-input'));
  41. self.el.searchClear = self.box.querySelector(classSelector('indexed-list-search') + ' ' + classSelector('icon-clear'));
  42. self.el.bar = self.box.querySelector(classSelector('indexed-list-bar'));
  43. self.el.barItems = [].slice.call(self.box.querySelectorAll(classSelector('indexed-list-bar') + ' a'));
  44. self.el.inner = self.box.querySelector(classSelector('indexed-list-inner'));
  45. self.el.items = [].slice.call(self.box.querySelectorAll(classSelector('indexed-list-item')));
  46. self.el.liArray = [].slice.call(self.box.querySelectorAll(classSelector('indexed-list-inner') + ' li'));
  47. self.el.alert = self.box.querySelector(classSelector('indexed-list-alert'));
  48. },
  49. caleLayout: function() {
  50. var self = this;
  51. var withoutSearchHeight = (self.box.offsetHeight - self.el.search.offsetHeight) + 'px';
  52. self.el.bar.style.height = withoutSearchHeight;
  53. self.el.inner.style.height = withoutSearchHeight;
  54. var barItemHeight = ((self.el.bar.offsetHeight - 40) / self.el.barItems.length) + 'px';
  55. self.el.barItems.forEach(function(item) {
  56. item.style.height = barItemHeight;
  57. item.style.lineHeight = barItemHeight;
  58. });
  59. },
  60. scrollTo: function(group) {
  61. var self = this;
  62. var groupElement = self.el.inner.querySelector('[data-group="' + group + '"]');
  63. if (!groupElement || (self.hiddenGroups && self.hiddenGroups.indexOf(groupElement) > -1)) {
  64. return;
  65. }
  66. self.el.inner.scrollTop = groupElement.offsetTop;
  67. },
  68. bindBarEvent: function() {
  69. var self = this;
  70. var pointElement = null;
  71. var findStart = function(event) {
  72. if (pointElement) {
  73. pointElement.classList.remove('active');
  74. pointElement = null;
  75. }
  76. self.el.bar.classList.add('active');
  77. var point = event.changedTouches ? event.changedTouches[0] : event;
  78. pointElement = document.elementFromPoint(point.pageX, point.pageY);
  79. if (pointElement) {
  80. var group = pointElement.innerText;
  81. if (group && group.length == 1) {
  82. pointElement.classList.add('active');
  83. self.el.alert.innerText = group;
  84. self.el.alert.classList.add('active');
  85. self.scrollTo(group);
  86. }
  87. }
  88. event.preventDefault();
  89. };
  90. var findEnd = function(event) {
  91. self.el.alert.classList.remove('active');
  92. self.el.bar.classList.remove('active');
  93. if (pointElement) {
  94. pointElement.classList.remove('active');
  95. pointElement = null;
  96. }
  97. };
  98. self.el.bar.addEventListener($.EVENT_MOVE, function(event) {
  99. findStart(event);
  100. }, false);
  101. self.el.bar.addEventListener($.EVENT_START, function(event) {
  102. findStart(event);
  103. }, false);
  104. document.body.addEventListener($.EVENT_END, function(event) {
  105. findEnd(event);
  106. }, false);
  107. document.body.addEventListener($.EVENT_CANCEL, function(event) {
  108. findEnd(event);
  109. }, false);
  110. },
  111. search: function(keyword) {
  112. var self = this;
  113. keyword = (keyword || '').toLowerCase();
  114. var selectorBuffer = [];
  115. var groupIndex = -1;
  116. var itemCount = 0;
  117. var liArray = self.el.liArray;
  118. var itemTotal = liArray.length;
  119. self.hiddenGroups = [];
  120. var checkGroup = function(currentIndex, last) {
  121. if (itemCount >= currentIndex - groupIndex - (last ? 0 : 1)) {
  122. selectorBuffer.push(classSelector('indexed-list-inner li') + ':nth-child(' + (groupIndex + 1) + ')');
  123. self.hiddenGroups.push(liArray[groupIndex]);
  124. };
  125. groupIndex = currentIndex;
  126. itemCount = 0;
  127. }
  128. liArray.forEach(function(item) {
  129. var currentIndex = liArray.indexOf(item);
  130. if (item.classList.contains($.className('indexed-list-group'))) {
  131. checkGroup(currentIndex, false);
  132. } else {
  133. var text = (item.innerText || '').toLowerCase();
  134. var value = (item.getAttribute('data-value') || '').toLowerCase();
  135. var tags = (item.getAttribute('data-tags') || '').toLowerCase();
  136. if (keyword && text.indexOf(keyword) < 0 &&
  137. value.indexOf(keyword) < 0 &&
  138. tags.indexOf(keyword) < 0) {
  139. selectorBuffer.push(classSelector('indexed-list-inner li') + ':nth-child(' + (currentIndex + 1) + ')');
  140. itemCount++;
  141. }
  142. if (currentIndex >= itemTotal - 1) {
  143. checkGroup(currentIndex, true);
  144. }
  145. }
  146. });
  147. if (selectorBuffer.length >= itemTotal) {
  148. self.el.inner.classList.add('empty');
  149. } else if (selectorBuffer.length > 0) {
  150. self.el.inner.classList.remove('empty');
  151. self.el.styleForSearch.innerText = selectorBuffer.join(', ') + "{display:none;}";
  152. } else {
  153. self.el.inner.classList.remove('empty');
  154. self.el.styleForSearch.innerText = "";
  155. }
  156. },
  157. bindSearchEvent: function() {
  158. var self = this;
  159. self.el.searchInput.addEventListener('input', function() {
  160. var keyword = this.value;
  161. self.search(keyword);
  162. }, false);
  163. $(self.el.search).on('tap', classSelector('icon-clear'), function() {
  164. self.search('');
  165. }, false);
  166. },
  167. bindEvent: function() {
  168. var self = this;
  169. self.bindBarEvent();
  170. self.bindSearchEvent();
  171. }
  172. });
  173. //mui(selector).indexedList 方式
  174. $.fn.indexedList = function(options) {
  175. //遍历选择的元素
  176. this.each(function(i, element) {
  177. if (element.indexedList) return;
  178. element.indexedList = new IndexedList(element, options);
  179. });
  180. return this[0] ? this[0].indexedList : null;
  181. };
  182. })(mui, window, document);