简介:有自己写的,也有网上看到的,即使是别人写的也会对其改动,或添加注释,并保持统一的编码风格,便于阅读。目标是不用第三方库即可完成(浏览器原生),简单做,能够阐述逻辑和原理清楚即可,因此可能考虑不是最周详的,包括跨浏览器的问题,如果你打算使用在项目中使用最好测试清楚,还可能要进一步修改之。
注意:打算提供在线例子的,但短时间内没有静态空间,所以例子的事情要稍等一阵子。已提供在线例子。
1、简易拖放效果
使用了 DOM 1 的方式登记事件,其实无必要 DOM Level 2 的 addEventListener,因为根据鼠标事件,同一时刻通常 document 只有一个 mousemove/mouseup 事件。点击查看例子。
<meta charset="utf-8" /> <title>拖放 DD</title> <script> SimpleDrag = function(el) { this.el = el; this._x = this._y = 0; el.onmousedown = delegate(this, this.start); this.move = delegate(this, this.move); function delegate(object, fn){ // 绑定当前 this,并且修正浏览器兼容问题 e || window.event return function(e) { return fn.call(object, (e || window.event)); } } }; SimpleDrag.prototype = { start : function(e) { this._x = e.clientX - this.el.offsetLeft; this._y = e.clientY - this.el.offsetTop; document.onmousemove = this.move; document.onmouseup = this.stop; }, // 拖动 move : function(e) { this.el.style.left = e.clientX - this._x + "px"; this.el.style.top = e.clientY - this._y + "px"; }, // 停止拖动 stop : function() { document.onmousemove = document.onmouseup = null; } }; </script> <style> .dragable{ background-color:#C4E3FD; width:50px; height:50px; position:absolute; left: 20px; cursor:move; } .dragEl_1{ top : 10px; border:5px solid blue; } .dragEl_2{ top : 80px; border:5px solid red; } </style> <div class="dragable dragEl_1">1</div> <div class="dragable dragEl_2">2</div> <script> new SimpleDrag(document.querySelector(".dragEl_1")); new SimpleDrag(document.querySelector(".dragEl_2")); </script>
2、上下滚动内容
原理是取出最尾元素放到前头。点击查看例子。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>上下滚动内容</title> </head> <body> <ul id="scroll"> <li>11111111111</li> <li>22222222222</li> <li>33333333333</li> <li>44444444444</li> <li>55555555555</li> <li>66666666666</li> <li>77777777777</li> <li>88888888888</li> <li>99999999999</li> <li>00000000000</li> </ul> <ol id="scrollA"> <li>11111111111</li> <li>22222222222</li> <li>33333333333</li> <li>44444444444</li> <li>55555555555</li> <li>66666666666</li> <li>77777777777</li> <li>88888888888</li> <li>99999999999</li> <li>00000000000</li> </ol> <script> /** * @param {Element} el 列表元素 * @param {Number} interval 动画时间间隔 * @param {Boolean} canstop 是否可以鼠标移入时候暂停动画 */ function ScrollContent(el, interval, canstop) { interval = interval || 3000; canstop = canstop || false; function scroll() { var lastEl = el.firstChild; while (lastEl.nodeType != 1) // 找到最后一个元素 lastEl = lastEl.nextSibling; el.appendChild(el.removeChild(lastEl)); // 把最后一个元素放到前头 } var t = window.setInterval(scroll, interval); if (canstop) { el.onmouseover = function() { if (t) window.clearInterval(t); } el.onmouseout = function() { t = window.setInterval(scroll, interval); } } } new ScrollContent(document.getElementById('scroll'), 1000); new ScrollContent(document.getElementById('scrollA'), 500, true); </script> </body> </html>
3、左右滚动内容
原理跟前者一样,只不过是把元素换为字符串,应该是更简单的了。点击查看在线例子。
<meta charset="utf-8" /> <title>水平字幕滚动</title> <div class="content1">这是一段滚动的文字11111111</div> <div class="content2">这是一段滚动的文字22222222</div> <script> /** * @param {Element} el 列表元素 * @param {Number} interval 动画时间间隔 * @param {Boolean} canstop 是否可以鼠标移入时候暂停动画 */ function ScrollContent_Hoz(el, interval, canstop) { interval = interval || 500; canstop = canstop || false; var arr = el.innerHTML.split(""); function scroll() { arr.push(arr.shift()); el.innerHTML = arr.join(""); } var t = window.setInterval(scroll, interval); if (canstop) { el.onmouseover = function() { if (t) window.clearInterval(t); } el.onmouseout = function() { t = window.setInterval(scroll, interval); } } } new ScrollContent_Hoz(document.querySelector('.content1')); new ScrollContent_Hoz(document.querySelector('.content2'), null, true); </script>
4、多级联动下拉菜单
联动本身并无多大难点,只是 onchange 事件的运用即可。值得一提的是这个演示了日历控件所涉及的日历生成算法——当然这是后话了。点击查看在线例子。
<meta charset="UTF-8"> <title>联动 Select 下拉</title> <select id="year"> <option value="0">--请选择--</option> </select>年 <select id="month"> <option value="0">--请选择--</option> </select>月 <select id="day"> <option value="0">--请选择--</option> </select>日 <script> function initSelect(selectEl, i, end) { selectEl.length = 1; for (; i <= end; i++) { try { selectEl.add(new Option(i, i), null); } catch (e) { selectEl.add(new Option(i, i)); } } } var year = document.getElementById("year"); var month = document.getElementById("month"); var day = document.getElementById("day"); initSelect(year, 1970, 2012); year.onchange = function() { if (this.value != 0) { initSelect(month, 1, 12); } else { month.length = 1; day.length = 1; } } month.onchange = function() { if (this.value != 0) { var m30 = { 2 : 1, 4 : 1, 6 : 1, 9 : 1, 11 : 1 }; if (this.value == 2) { // 二月份特别处理 if (isLeapYear(year.value)) initSelect(day, 1, 29); else initSelect(day, 1, 28); } else if (this.value in m30) // 三十天的月份 /* 因为2、4、6、9、11月份都是30天,如果把它们放在一个数组中,那么还要遍历来判断是否相等,则比较麻烦了,于是在这里把这些都当成对象来处理,使用 in 判断即可 */ initSelect(day, 1, 30); else initSelect(day, 1, 31); } else day.length = 1; } // 判断闰年的条件:能被4整除且不能被100整除 或 能被100整除且能被400整除 function isLeapYear(y) { return (y % 4 == 0 && y % 100 != 0) || (y % 100 == 0 && y % 400 == 0); } </script>
5、Tab 多选项卡
主要控制 tab display:block/none 来显示与隐藏,原理比较简单。使用方法:new SimpleTab(document.querySelector('.tab2')).jump(0);。点击查看在线例子。
代码比较长,所以只贴 JS 部分。完整代码参见 HTML。
/////////////////////--------------------公共方法 -------------START var emptyStr = ''; // 允许用户输入 .abc/abc 的 CSS Selector,推荐 abc 即可 function removeFirstDot(str){ if(str && str.charAt(0) === '.'){ var arr = str.split(emptyStr); arr.shift(); return arr.join(emptyStr); } return str; } /** * 增加元素样式。 * @param {String} cls */ Element.prototype.addCls = function (cls) { cls = removeFirstDot(cls); var _cls = this.className; if (_cls.indexOf(cls) === -1) { // not found, so add it if (_cls === '')this.className = cls; else this.className += ' ' + cls; } } /** * 移除元素样式。 * @param {String} cls */ Element.prototype.removeCls = function (cls) { cls = removeFirstDot(cls); var _cls = this.className; var reg = new RegExp('\\s?\\b' + cls + '\\b', 'ig'); if (reg.test(_cls)) this.className = _cls.replace(reg, '');// remove it } /////////////////////--------------------公共方法 -------------END SimpleTab = function(container) { this.el = container; var ul = container.querySelector('ul'); this.buttons = ul.children, // tab候选栏strip this.tabs = container.querySelector('.content').children; ul.onclick = onTabChooserPressHandler.bind(this); } /** * 跳到指定的 tab,仿佛好象点击那样 * @param {int} index */ SimpleTab.prototype.jump = function(index){ var btn = this.buttons[index]; onTabChooserPressHandler.call(this, { target : btn, currentTarget : this.el.querySelector('ul') }); } var onPressed_ClassName = 'selected'; // 登记的单击事件是整个 tan panel function onTabChooserPressHandler(e) { // 搜索 el 下的 li 元素,到容器为止 var el = e.target; if (el.tagName != 'LI') return; var buttons = e.currentTarget.children, // tab候选栏strip tabs = e.currentTarget.parentNode.querySelector('.content').children; !buttons.length && console.log('该控件未发现任何 strip。'); for (var nextIndex = 0, j = buttons.length; nextIndex < j; nextIndex++) if (buttons[nextIndex] == el) break; // 获取 nextIndex // 查找与 index 相等的 item 设置其高亮,否则移除样式。 var btn, showTab; for (var i = 0, j = buttons.length; i < j; i++) { btn = buttons[i], showTab = tabs[i]; // debugger; if (nextIndex == i && btn.className.indexOf(onPressed_ClassName) == -1) { // 找到目标项 btn.addCls(onPressed_ClassName); showTab.addCls(onPressed_ClassName); this.currentIndex = i; // 保存当前游标 } else if (btn == el && btn.className.indexOf(onPressed_ClassName) != -1) { // 已在当前项 } else if (btn.className.indexOf(onPressed_ClassName) != -1) { btn.removeCls(onPressed_ClassName); showTab.removeCls(onPressed_ClassName); } } } new SimpleTab(document.querySelector('.tab2')).jump(0);
6、全屏幕遮罩及 ESC 键盘事件响应
一般这是全局性的组件,设置为“单例”即可。全屏幕基本是 CSS 还有一个 JS 的小尾巴,因为似乎用 CSS 不容易设置高度,用 JS 做还是比较可行的。点击查看在线例子。
<meta charset="UTF-8"> <title>全屏幕遮罩及 ESC 键盘事件响应</title> <div class="mask"></div> <style> .mask { position: fixed; top: 0; left: 0; width: 100%; z-index: 9999999; background-color: rgba(0, 0, 0, 0.5); } </style> <script> // 获取页面内容高度赋予 mask document.querySelector('.mask').style.height = document.body.scrollHeight + 'px'; // 键盘事件 function onEnterAndEsc(e){ e = e || event; var keycode = e.which || e.keyCode; switch(keycode){ case 13: //enter // 如果 form 里有 action,按下回车自动提交 // ... break; case 339: //exit case 340: //back case 27: var mask = document.querySelector('.mask'); mask.parentNode.removeChild(mask); } } document.onkeydown = onEnterAndEsc; </script>
7、消息对话框
和这里其他的组件一样,对话框也是一个非常雏形的组件,仅仅完成了 fixed 定位、居中、关闭这几样功能,——可能样式方面的代码还着墨的比较多。至于背景遮罩,前面一个例子就有,有兴趣的童鞋可以看看怎么把两者组装起来。另外对话框拖放也是一个功能点,咱们在第一个例子的时候已经有介绍了。动手能力强的童鞋也可以试着自己把它们封装在一起,形成完整的对话框组件——这里就不再赘述了。点击查看在线例子。
<meta charset="UTF-8"> <title>MsgBox</title> <div class="msgbox"> <h1>确认</h1> <div class="topCloseBtn closeAction">×</div> <div class="inner"> 益力多,你今日饮左未? </div> <div class="btn"> <div class="closeAction">确认</div> </div> </div> <style> .msgbox { position: fixed; background-color: #ebebeb; border: 1px solid #d3d3d3; width: 50%; min-width: 300px; max-width: 400px; z-index: 99999999; margin: auto; min-height: 100px; } .msgbox h1 { font-size: .9rem; text-align: center; padding: 5px 0; letter-spacing: 3px; } .msgbox .topCloseBtn { position: absolute; text-align: center; top: 0; right: 10px; width: 16px; line-height: 16px; height: 16px; background-color: gray; color: white; cursor: pointer; } .msgbox .inner { margin: 5px; margin-top: 0; padding: 10px; overflow: hidden; background-color: #f7f7f7; text-align: center; border: 1px solid #dadada; min-height:50px; line-height:50px; } .msgbox .btn { margin: 5px auto; width: 70%; overflow: hidden; text-align: center; } .msgbox .btn > div { display: inline-block; max-width: 80px; cursor: pointer; text-align: center; border: 1px solid #d3d3d3; padding: 3px 10px; letter-spacing: 3px; box-sizing: border-box; background-color: #f7f7f7; font-size: .9rem; } </style> <script> function MsgBox(msgbox) { // 计算居中 msgbox.style.left = (window.innerWidth / 2 - msgbox.clientWidth / 2) + 'px'; msgbox.style.top = (window.innerHeight / 2 - msgbox.clientHeight / 2) + 'px'; // 平均分按钮宽度 var btn = msgbox.querySelector('.btn'), btns = msgbox.querySelectorAll('.btn>div'); var j = btns.length, width = btn.clientWidth / j - 12; // 要减去间隙 // 登记关闭事件 [].forEach.call(msgbox.querySelectorAll('.closeAction'), function(closeBtn){ closeBtn.onclick = function(){ msgbox.parentNode.removeChild(msgbox); } }); } new MsgBox(document.querySelector('.msgbox')); </script>
8、跑马灯 Carousel 轮播
跑马灯是个经典的效果,几乎每个网站、APP 都会用到,我们应该要好好掌握跑马灯实现的算法。一种最普遍的思路:把图片们用 ul 之类(div 亦可)的包起来,并设置 float,然后设置这个 ul 本身为 absolute 定位,其父标签用 relative 定位。通过设置 ul 的 left 或 top 值,从而实现图片队列的滚动效果。 点击查看在线例子。
<meta charset="UTF-8"> <title>carousel</title> <div class="carousel"> <div> <div> <a href="topic/?id=283"> <img src="http://imgu.3gtv.net:9090/_file/section/20150919114717260.jpg"> </a> </div> <div> <a href="live/?id=3"> <img src="http://imgu.3gtv.net:9090/_file/section/20150918214948448.png"> </a> </div> <div> <a href="topic/?id=346"> <img src="http://imgu.3gtv.net:9090/_file/section/20150910171423733.png"> </a> </div> <div> <a href="topic/?id=345"> <img src="http://imgu.3gtv.net:9090/_file/section/20150909172539065.jpg"> </a> </div> </div> </div> <style> .carousel{ height:250px; width:600px; overflow:hidden; position: relative; } .carousel > div{ left:0; position:absolute; white-space: nowrap; transition: 200ms left linear; -moz-transition: 200ms left linear; -o-transition: 200ms left linear; -ie-transition: 200ms left linear; -webkit-transition: 200ms left linear; } .carousel > div > div{ float: left; } .carousel > div > div img { width: 100%; } </style> <script> function Carousel(el) { var containerWidth = el.clientWidth, mover = el.querySelector('div'), items = mover.children, len = items.length; // 设置 mover 和 item 宽度。初始化时候运行。 for (var i = 0; i < len; i++) { items[i].style.width = containerWidth + 'px'; // 设置item 宽度 } mover.style.width = containerWidth * len + 'px'; // 总宽度,也是容器中最宽的宽度。赋予给 mover。 var count = 0; setInterval(function(){ if(++count == len)count = 0; mover.style.left = -count * containerWidth + 'px'; }, 2000); } new Carousel(document.querySelector('.carousel')); </script>