@autofe/carpicker
Version:
A Carpicker library based on jQuery
440 lines (374 loc) • 13.6 kB
JavaScript
/*!
* @autofe/carpicker v0.2.0
* (c) 2018 Autohome Inc.
* Released under the MIT License.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) :
typeof define === 'function' && define.amd ? define(['jquery'], factory) :
(global.AutoFE = global.AutoFE || {}, global.AutoFE.Carpicker = factory(global.jQuery));
}(this, (function ($) { 'use strict';
$ = $ && $.hasOwnProperty('default') ? $['default'] : $;
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'carpicker';
var DATA_KEY = 'fe.carpicker';
var EVENT_KEY = '.' + DATA_KEY;
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
// const ESCAPE_KEYCODE = 27;
var Event = {
INIT: 'init' + EVENT_KEY,
SHOW: 'show' + EVENT_KEY,
SHOWN: 'shown' + EVENT_KEY,
HIDE: 'hide' + EVENT_KEY,
HIDDEN: 'hidden' + EVENT_KEY,
CHANGE: 'change' + EVENT_KEY,
CLICK: 'click' + EVENT_KEY,
CLICK_DISMISS: 'click.dismiss' + EVENT_KEY,
CLICK_DATA_API: 'click' + EVENT_KEY + DATA_API_KEY
};
var Selector = {
DATA_TOGGLE: '[data-toggle="carpicker"]',
DATA_VALUE: '[data-value]'
};
var Attr = {
DATA_TEXT: 'data-text',
DATA_VALUE: 'data-value'
};
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
function Carpicker(elem, options) {
this.options = $.extend({}, Carpicker.Default, options);
this.$elem = $(elem);
this.$picker = $(this.options.selectPicker, this.$elem);
this.$value = $(this.options.selectValue, this.$elem);
this.$dropdown = $(this.options.selectDropdown, this.$elem);
this.$brand = $(this.options.selectBrand, this.$elem);
this.$series = $(this.options.selectSeries, this.$elem);
this.$spec = $(this.options.selectSpec, this.$elem);
this.$level = this.options.selectLevel;
this.isActive = false;
this._data = null;
this._brandData = null;
this._init();
}
Carpicker.Default = {
selectPicker: '[data-select-picker]',
selectValue: '[data-select-value]',
selectDropdown: '[data-select-dropdown]',
selectBrand: '[data-select-brand]',
selectSeries: '[data-select-series]',
selectSpec: '[data-select-spec]',
selectedClass: 'selected',
disabledClass: 'disabled',
activeClass: 'active',
selectNav: false,
selectLevel: 'brand',
onInitPicker: null,
onBrandPicker: null,
onSeriesPicker: null,
onSpecPicker: null
};
Carpicker.prototype._init = function () {
var that = this;
that.$elem.trigger(Event.INIT, that);
that.$picker.on(Event.CLICK, $.proxy(that.toggle, that));
// 如果设置了品牌车系车型导航,创建导航并绑定点击事件
if (that.options.selectNav) {
if (that.$brand.prev('ul.pop-nav').length == 0) {
that.$brand.parent().prepend('<ul class="pop-nav"></ul>');
}
that.$dropdown.on(Event.CLICK, 'em', function () {
var $item = $(this).parent();
switch ($item.index()) {
case 0:
that.$brand.show();
that.$series.hide();
that.$spec.hide();
that.$brand.prev('.pop-nav').html('<li><em>\u54C1\u724C</em></li>');
break;
case 1:
that.$brand.hide();
that.$series.show();
that.$spec.hide();
that.$brand.prev('.pop-nav').html('<li><em>\u54C1\u724C</em></li>\n <li><span>></span><em>\u8F66\u7CFB</em></li>');
break;
case 2:
break;
default:
break;
}
return false;
});
}
that.$dropdown.on(Event.CLICK, Selector.DATA_VALUE, function () {
var $item = $(this);
var $target = $item.attr('data-target');
// 点击品牌并触发对应回调函数
if ($target === 'brand' && typeof that.options.onBrandPicker === 'function') {
that.options.onBrandPicker($item.attr(Attr.DATA_VALUE), this);
}
// 点击车系并触发对应回调函数
if ($target === 'series' && typeof that.options.onSeriesPicker === 'function') {
that.options.onSeriesPicker($item.attr(Attr.DATA_VALUE), this);
}
// 点击车型并触发对应回调函数
if ($target === 'spec' && typeof that.options.onSpecPicker === 'function' && !$item.hasClass('disabled')) {
that.options.onSpecPicker($item.attr(Attr.DATA_VALUE), this);
}
// 设置最多选择到品牌参数时,点击品牌选中并收起下拉框
if ($target === 'brand' && that.options.selectLevel == 'brand') {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
// 车系
if ($target === 'series' && (that.options.selectLevel == 'series' || that.options.selectLevel == 'spec' && !that.options.selectNav)) {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
// 车型
if ($target === 'spec' && !$item.hasClass('disabled')) {
that.setValue({
text: $item.attr(Attr.DATA_TEXT),
value: $item.attr(Attr.DATA_VALUE)
});
that.hide();
}
});
$(document).on(Event.CLICK_DISMISS, function (e) {
var $parent = $(e.target).closest(that.$elem);
if ($parent.length === 0 && that.isActive) {
that.hide();
}
});
if (typeof that.options.onInitPicker === 'function') {
that._brandData = that.options.onInitPicker();
that.setBrand();
}
};
Carpicker.prototype.setValue = function (data) {
var selectedClass = this.options.selectedClass;
var old = this.getValue() || {};
if (old.value !== data.value) {
var $list = this.$dropdown.find(Selector.DATA_VALUE);
var $item = this.$dropdown.find('[data-value="' + String(data.value) + '"]');
$list.removeClass(selectedClass);
$item.addClass(selectedClass);
this.$value.text(data.text);
this._data = {
text: data.text,
value: data.value
};
this.$elem.trigger(Event.CHANGE, [data, $item]);
}
};
Carpicker.prototype.getValue = function () {
return this._data;
};
Carpicker.prototype.toggle = function () {
if (this.isActive) {
this.hide();
} else {
this.show();
}
};
Carpicker.prototype.show = function () {
var activeClass = this.options.activeClass;
var disabledClass = this.options.disabledClass;
if (this.$elem.hasClass(disabledClass)) {
return;
}
var showEvent = $.Event(Event.SHOW);
this.$elem.trigger(showEvent, this);
if (showEvent.isDefaultPrevented()) {
return;
}
this.isActive = true;
this.$elem.addClass(activeClass);
this.$dropdown.show();
this.$elem.trigger(Event.SHOWN, this);
};
Carpicker.prototype.hide = function () {
var activeClass = this.options.activeClass;
var hideEvent = $.Event(Event.HIDE);
this.$elem.trigger(hideEvent, this);
if (hideEvent.isDefaultPrevented()) {
return;
}
this.isActive = false;
this.$elem.removeClass(activeClass);
this.$dropdown.hide();
this.$elem.trigger(Event.HIDDEN, this);
};
Carpicker.prototype.disable = function () {
var disabledClass = this.options.disabledClass;
this.$elem.addClass(disabledClass);
};
Carpicker.prototype.enable = function () {
var disabledClass = this.options.disabledClass;
this.$elem.removeClass(disabledClass);
};
Carpicker.prototype.setBrand = function () {
var that = this;
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html('<li><em>\u54C1\u724C</em></li>');
}
// 字母索引
var alphabeta = [];
that._brandData.forEach(function (elem) {
if (!alphabeta.includes(elem.letter)) {
alphabeta.push(elem.letter);
}
});
if ($.trim(that.$brand.html()) === '') {
var brandIndex = '<ol class="index">';
var brandList = '<dl class="list">';
for (var i = 0; i < alphabeta.length; i++) {
// 左侧字母
brandIndex += '<li data-target=\'jump-' + String(alphabeta[i]) + '\'>' + String(alphabeta[i]) + '</li>';
// 右侧内容
brandList += '<dt id=\'jump-' + String(alphabeta[i]) + '\'>' + String(alphabeta[i]) + '</dt>';
that._brandData.forEach(function (brand) {
if (brand.letter == alphabeta[i]) {
brandList += '<dd data-value="' + String(brand.id) + '" \n data-text="' + String(brand.name) + '" \n ' + (brand.pinyin ? 'data-pinyin=' + String(brand.pinyin) : '') + '\n data-target="brand">' + String(brand.name) + '</dd>';
}
});
}
brandIndex += '</ol>';
brandList += '</dl>';
that.$brand.html(brandIndex + brandList);
that.setIndexLoca();
}
that.$brand.show();
};
Carpicker.prototype.setSeries = function (data, seriesItem) {
var that = this;
if (data.length > 0) {
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html('<li><em>\u54C1\u724C</em></li>\n <li><span>></span><em>\u8F66\u7CFB</em></li>');
that.$brand.hide();
}
that.$series.show();
var seriesStr = '<dl class="list">';
data.forEach(function (series, i) {
// 全部车系项
if (seriesItem && seriesItem.show && i == 0) {
if (seriesItem.link) {
seriesStr += '<dd><a class="all" href="' + String(seriesItem.url) + '" target="_blank">\u5168\u90E8\u8F66\u7CFB</a></dd>';
} else {
seriesStr += '<dd data-value="' + String(series.brandId) + '" data-text="\u5168\u90E8\u8F66\u7CFB" data-target="series">\u5168\u90E8\u8F66\u7CFB</dd>';
}
}
seriesStr += '<dt>' + String(series.name) + '</dt>';
series.list.forEach(function (item) {
seriesStr += '<dd data-value="' + String(item.id) + '" \n data-text="' + String(item.name) + '" \n ' + (item.pinyin ? 'data-pinyin=' + String(item.pinyin) : '') + '\n data-target="series">' + String(item.name) + '</dd>';
});
});
seriesStr += '</dl>';
that.$series.html(seriesStr);
return;
}
};
Carpicker.prototype.setSpec = function (data, selectSpec) {
var that = this;
if (that.options.selectNav) {
that.$brand.prev('.pop-nav').html('<li><em>\u54C1\u724C</em></li>\n <li><span>></span><em>\u8F66\u7CFB</em></li>\n <li><span>></span><em>\u8F66\u578B</em></li>');
that.$brand.hide();
that.$series.hide();
}
that.$spec.show();
var specStr = '<dl class="list">';
if (data.length > 0) {
data.forEach(function (item) {
specStr += '<dt>' + String(item.name) + '</dt>';
item.list.forEach(function (spec) {
specStr += '<dd data-value="' + String(spec.id) + '" \n data-text="' + String(spec.name) + '"\n data-target="spec"\n ' + (selectSpec && $.isArray(selectSpec) && selectSpec.includes(spec.id.toString()) ? 'class="disabled"' : '') + '>\n ' + String(spec.name) + '\n ' + (spec.price ? '<span>' + (spec.price > 0 ? (spec.price / 10000.0).toFixed(2) + '\u4E07' : '\u6682\u65E0') + '</span>' : '') + '\n </dd>';
});
});
specStr += '<dd></dd>';
} else {
specStr += '<dd>\u6682\u65E0\u76F8\u5173\u8F66\u578B</dd>';
}
specStr += '</dl>';
that.$spec.html(specStr);
return;
};
Carpicker.prototype.setIndexLoca = function () {
var that = this;
that.$brand.on(Event.CLICK, 'li[data-target^="jump"]', function () {
$('.list', that.$brand).scrollTop(0);
$(this).addClass('active').siblings().removeClass('active');
var target = $(this).attr('data-target');
var targetTop = $('dt#' + target, that.$brand).position("list").top;
$('.list', that.$brand).scrollTop(targetTop);
});
};
Carpicker.prototype.destroy = function () {
this.$elem.removeData(DATA_KEY);
this.$picker.off(Event.CLICK);
this.$dropdown.off(Event.CLICK, Selector.DATA_VALUE);
this.onInitPicker = null, this.onBrandPicker = null, this.onSeriesPicker = null, this.onSpecPicker = null;
this._brandData = null, this.setValue({});
};
/**
* ------------------------------------------------------------------------
* Plugin Definition
* ------------------------------------------------------------------------
*/
function Plugin(config) {
return this.each(function () {
var $this = $(this);
var data = $this.data(DATA_KEY);
var _config = $.extend({}, Carpicker.Default, $this.data(), typeof config === 'object' && config);
if (!data) {
data = new Carpicker(this, _config);
$this.data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError('No method named "' + config + '"');
}
data[config]();
}
});
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
// TODO
// $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (e) {
// e.preventDefault();
// e.stopPropagation();
// Plugin.call($(this), 'toggle');
// });
$(function () {
Plugin.call($(Selector.DATA_TOGGLE));
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Plugin;
$.fn[NAME].Constructor = Carpicker;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Plugin;
};
return Carpicker;
})));