最近公司碰到一个需求,基于jq 开发一个通用的 带建议 的input 控件;
忙乱中写出了一个通用结构和方法,跟大家分享一下,同时也算做自己的踩坑总结吧。
通用一个模板,不影响原有 input 所在结构;
因为原来的表单中 已经有固定结构的 input 了; 所以不能改html结构;
大概的思路就是,定义一个option 包裹层;然后点击需要带建议的input的时候,去根据自定义属性 suggest-url 拿到对应接口,取数据回来遍历出选项。然后根据 这个input 的top,left 做绝对定位显示出来。
//以下是固有的 form input 结构
<div class="am-input-group am-input-group-sm">
<span class="am-input-group-label form-title pl42">单位</span>
<input name="unit" type="text" class="am-form-field suggest-input" suggest-url="xxx" value="吨">
<span class="am-input-group-label">(默认)</span>
</div>
//随意放一个包裹层
<div class="suggest-option">
<ul></ul>
</div>
逐步分解 实现步骤
希望只调用一个函数实现功能
全局 有 suggest-input 类 的input 都实现option 功能;
function initEvent() {
suggestInput();
})
函数实现与解析 如下:
function suggestInput() {
//在input focus 的时候做处理。
$(".suggest-input").focus(function () {
//获取相关 接口的值
var url = $(this).attr("suggest-url");
//缓存操作对象
var $input = $(this);
var $suggest = $(".suggest-option");
var $ul = $suggest.find("ul");
//通过input 找到 option 应该显示的top 和left
var top = $input.offset().top + $input.outerHeight()+5;
var left = $input.offset().left;
var width = $input.outerWidth();
//如下是基于 url 的ajax请求,此处暂时用静态数据
/*doAjaxPost(url,'',function (resp) {
if(resp.status==1){
var dataObj = resp.data;
$ul.html('');
dataObj.forEach(function (p1) {
$ul.append("<li id='"+p1.id+"'>"+p1.title+"</li>");
});
}else {
$ul.append("<li>暂无数据</li>")
return;
}
});*/
var dataObj = [{id:1,title:"吨"},{id:1,title:"克"},{id:1,title:"元"}];
//先清空 ul;
$ul.html('');
//遍历产生 option
dataObj.forEach(function (p1) {
$ul.append("<li id='"+p1.id+"'>"+p1.title+"</li>");
});
//设置 option框 应该显示的位置
$suggest.css({
top:top,
left:left,
width:width
}).show();
//找到发生滚动事件的 包裹层,定制滚动时的处理
$input.parents().each(function (i,o) {
if($(o).css('overflow-y')=="auto"){
return $(o);
}
}).scroll(function () {
$suggest.css({
top:$input.offset().top + $input.outerHeight()+5,
})
});
//点击消失相关操作
$input.click(function (e) {
e.stopPropagation();
})
$("body").click(function () {
$suggest.hide();
})
//选中option 时回填数据
$suggest.find('li').unbind('click').click(function (e) {
$input.val($(this).text());
$suggest.hide();
e.stopPropagation();
})
})
}
效果如下:
遇到的坑
整个代码逻辑下来 其实并不难理解,但是有两个需要注意到的地方;
每次为点击li绑定事件的时候,应该先解除之前绑定的相同事件。
$suggest.find('li').unbind('click').click(function (e) {
})
如果不解绑,会导致你改变 其中一个 input 的值,其它input都改变; 因为你初始化的时候,为每一个li 都绑定了click 事件。
当表单出现滚动的时候,option 框没有跟着 移动;
$input.parents().each(function (i,o) {
if($(o).css('overflow-y')=="auto"){
return $(o);
}
}).scroll(function () {
$suggest.css({
top:$input.offset().top + $input.outerHeight()+5,
})
});
解决办法就是 检查 发生滚动的box ;发生滚动的时候,重新计算一次 option 的 top;
不过如果手动绑定 scroll 监听,会容易有两个问题。一,可能发生滚动的 box 不是你监听的box; 二,即使你知道发生滚动的div,如果直接写死的话,发生结构变化时,代码不够通用。
因为我通过遍历 input 的父子集,找到最近一个 overflow-y:auto 的父box;这就是发生滚动的父box。
然后绑定事件。
最后效果: