diff --git a/docs/form/index.md b/docs/form/index.md index 108145c3..cac7d0d1 100644 --- a/docs/form/index.md +++ b/docs/form/index.md @@ -118,6 +118,8 @@ form 还可以借助*栅格*实现更灵活的响应式布局。 | lay-skin | [#详见](checkbox.html#default) | 设置 UI 风格。 ``,`` 元素 **私有属性** | | lay-search | 默认不区分大小写;
设置`cs`区分大小写 | 给 `select` 组件开启搜索功能。`` 元素 **私有属性** | +| lay-append-to 2.9.12+ 实验性 | `body` | 是否将 select 面板追加到 body 元素中。`` 元素 **私有属性** | | lay-submit | 无需值 | 设置元素(一般为` @@ -370,10 +371,12 @@ // 定位类型 var position = opts.position; if(position) elem.style.position = position; + var offsetX = opts.offset ? opts.offset[0] : 0; + var offsetY = opts.offset ? opts.offset[1] : 0; // 设置坐标 - elem.style.left = left + (position === 'fixed' ? 0 : scrollArea(1)) + 'px'; - elem.style.top = top + (position === 'fixed' ? 0 : scrollArea()) + 'px'; + elem.style.left = left + (position === 'fixed' ? 0 : scrollArea(1)) + offsetX + 'px'; + elem.style.top = top + (position === 'fixed' ? 0 : scrollArea()) + offsetY + 'px'; // 防止页面无滚动条时,又因为弹出面板而出现滚动条导致的坐标计算偏差 if(!lay.hasScrollbar()){ @@ -676,6 +679,99 @@ } }(); + /** + * 监听指定元素外部的点击 + * @param {HTMLElement} target - 被监听的元素 + * @param {(e: Event) => void} handler - 事件触发时执行的函数 + * @param {object} [options] - 选项 + * @param {string} [options.event="pointerdown"] - 监听的事件类型 + * @param {HTMLElement | Window} [options.scope=document] - 监听范围 + * @param {Array} [options.ignore] - 忽略监听的元素或选择器字符串 + * @param {boolean} [options.capture=true] - 对内部事件侦听器使用捕获阶段 + * @returns {() => void} - 返回一个停止事件监听的函数 + */ + lay.onClickOutside = function(target, handler, options){ + options = options || {}; + var eventType = options.event || ('onpointerdown' in window ? 'pointerdown' : 'mousedown'); + var scopeTarget = options.scope || document; + var ignore = options.ignore || []; + var useCapture = 'capture' in options ? options.capture : true; + + var listener = function(event){ + var el = target; + var eventTarget = event.target || event.srcElement; + var eventPath = getEventPath(event); + + if (!el || el === eventTarget || eventPath.indexOf(el) !== -1){ + return; + } + if(shouldIgnore(event, eventPath)){ + return; + } + + handler(event); + }; + + function shouldIgnore(event, eventPath){ + var eventTarget = event.target || event.srcElement; + for(var i = 0; i < ignore.length; i++){ + var target = ignore[i]; + if(typeof target === 'string'){ + var targetElements = document.querySelectorAll(target); + for(var j = 0; j < targetElements.length; j++){ + var targetEl = targetElements[i]; + if(targetEl === eventTarget || eventPath.indexOf(targetEl) !== -1){ + return true; + } + } + }else{ + if(target && (target === eventTarget || eventPath.indexOf(target) !== -1)){ + return true; + } + } + } + } + + function getEventPath(event){ + var path = (event.composedPath && event.composedPath()) || event.path; + var eventTarget = event.target || event.srcElement; + + if (path !== null && path !== undefined){ + return path; + } + + function getParents(node, memo){ + memo = memo || []; + var parentNode = node.parentNode; + + return parentNode + ? getParents(parentNode, memo.concat([parentNode])) + : memo; + } + + return [eventTarget].concat(getParents(eventTarget)); + } + + function bindEventListener(elem, eventName, handler, opts){ + elem.addEventListener + ? elem.addEventListener(eventName, handler, opts) + : elem.attachEvent('on' + eventName, handler); + + return function(){ + elem.removeEventListener + ? elem.removeEventListener(eventName, handler, opts) + : elem.detachEvent('on' + eventName, handler); + } + } + + return bindEventListener( + scopeTarget, + eventType, + listener, + lay.passiveSupported ? { passive: true, capture: useCapture } : useCapture + ); + }; + /* * lay 元素操作