小方法
2019-10-14 本文已影响0人
田成力
封装animate
/*==ANIMATE动画库==*/
~function () {
//=>准备操作CSS样式的方法 GET-CSS/SET-CSS/SET-GROUP-CSS/CSS
let utils = (function () {
//=>获取样式
let getCss = (ele, attr) => {
let val = null,
reg = /^-?\d+(\.\d+)?(px|rem|em)?$/;
if ('getComputedStyle' in window) {
val = window.getComputedStyle(ele)[attr];
if (reg.test(val)) {
val = parseFloat(val);
}
}
return val;
};
//=>设置样式
let setCss = (ele, attr, value) => {
if (!isNaN(value)) {
if (!/^(opacity|zIndex)$/.test(attr)) {
value += 'px';
}
}
ele['style'][attr] = value;
};
//=>批量设置样式
let setGroupCss = (ele, options) => {
for (let attr in options) {
if (options.hasOwnProperty(attr)) {
setCss(ele, attr, options[attr]);
}
}
};
//=>合并为一个
let css = (...arg) => {
let len = arg.length,
fn = getCss;
if (len >= 3) {
fn = setCss;
}
if (len === 2 && typeof arg[1] === 'object') {
fn = setGroupCss;
}
return fn(...arg);
};
return {css}
})();
//=>EFFECT:准备运动的公式
let effect = {
Linear: (t, b, c, d) => t / d * c + b
};
//=>封装动画库
window.animate = function (ele, target = {}, duration = 1000,callback=new Function()) {
//1.基于TARGET计算出BEGIN/CHANGE
let begin = {},
change = {},
time = 0;
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
begin[attr] = utils.css(ele, attr);
change[attr] = target[attr] - begin[attr];
}
}
//2.实现动画
clearInterval(ele.animteTimer);//=>在给当前元素设置新的动画之前,先清空原有正在运行的动画(防止多动画共存,把动画的返回值赋值给当前元素的自定义属性,这样只要元素不变,我们不管啥时候在哪执行都可以清除元素的动画)
ele.animteTimer = setInterval(() => {
time += 17;
//=>边界判断
if (time >= duration) {
utils.css(ele, target);
clearInterval(ele.animteTimer);
callback.call(ele);//动画完成后让执行callback并且让this指向元素本身
return;
}
//=>依托TARGET计算出每个方向的当前位置
let cur = {};
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
cur[attr] = effect.Linear(time, begin[attr], change[attr], duration);
}
}
utils.css(ele, cur);
}, 17);
};
}();
封装each
//=>OBJ:我们需要迭代的数组、类数组、对象
let each = function (obj, callback) {
//=>验证是数组(类数组)还是对象
let flag = 'length' in obj;//=>我们先简单验证:有LENGTH是数组或者类数组,没有是对象
if (flag) {
for (let i = 0; i < obj.length; i++) {
let item = obj[i];
let res = callback && callback.call(item, i, item);//=>接收回调函数的返回值,如果返回的是FALSE,我们结束循环即可
if (res === false) {
break;
}
}
} else {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key],
res = callback && callback.call(value, key, value);
if (res === false) {
break;
}
}
}
}
};
each([12, 23, 34], function (index, item) {
//=>this:item
// console.log(index, item, this);
// return false;//=>如果回调函数返回FALSE,我们让其代表:结束当前迭代
console.log(item);
if (index >= 1) {
return false;
}
});
each({name: 'xxx', age: 12, sex: 0}, function (key, value) {
console.log(key, value, this);
if (key === 'age') {
return false;
}
});
封装replace
String.prototype.myReplace = function myReplace(reg, callback) {
//=>this:str
//=>默认REG肯定加G了,CALLBACK肯定传递函数了
let res = reg.exec(this),
_this = this;
while (res) {
//=>res:每一次EXEC捕获的结果(数组)
let returnV = callback(...res);//=>捕获一次执行一次回调函数,并且把通过EXEC捕获的数组展开,每一项都依次传递给回调函数(returnV:当前回调函数执行的返回结果,我们要拿这个结果替换字符串中当前大正则匹配的内容)
let v = res[0],
i = _this.indexOf(v);
_this = _this.substring(0, i) + returnV + _this.substring(v.length + i);
res = reg.exec(this);
}
return _this;
};
let str = 'my name is {0},i am {1} years old!',
ary = ['周啸天哈哈', '28'];
str = str.myReplace(/\{(\d+)\}/g, function (...arg) {
let index = arg[1];
return ary[index];
});
console.log(str);
// 'my name is {0},i am {1} years old!' "周啸天"
// ['{0}','0',index:11]
原型链机制数组去重
~function () {
var pro = Array.prototype;
//=>数组去重
pro.myDistinct = function myDistinct() {
var obj = {};
for (var i = 0; i < this.length; i++) {
var item = this[i];
if (typeof obj[item] !== 'undefined') {
this[i] = this[this.length - 1];
this.length--;
i--;
continue;
}
obj[item] = item;
}
obj = null;
return this;
};
}();
选项卡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
.tab {
width: 600px;
margin: 20px auto;
}
.tab ul {
text-align: center;
}
.tab ul li {
width: 150px;
height: 40px;
line-height: 40px;
display: inline-block;
border: 1px solid black;
}
.tab div {
border: 1px solid black;
height: 200px;
background: green;
display: none;
}
.tab .selectLi {
background: green;
height: 40px;
width: 150px;
}
.tab .selectDiv {
background: green;
display: block;
}
</style>
</head>
<body>
<div id="tab1" class="tab">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<div id="tab2" class="tab">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
<script>
//1.用哪种方式封装插件 ->构造函数+原型的方式
//2.哪些参数是必须传的,哪些是可选的
//必传:最外层的id 可选:默认下哪个是选中的,若手动设置则传选中的li的索引,不传则是第一个默认选中
//3.插件的功能有哪些?
//1.获取元素 2.绑定点击事件 3.实现点击时的逻辑
function Tab(options){
//哪些是私有的,哪些的是公有的
if(options.id == undefined){return;}
this.id = options.id;
this.select = options.select || 0;
this.init();
}
Tab.prototype.init = function(){
//初始化插件的功能,页面一加载时就运行
//1.获取元素
this.getEle();
//2.绑定点击事件
this.bind();
//3.实现点击时的逻辑(点击时运行)
//设置默认选中的li和Div
this.oLis[this.select].className = "selectLi";
this.oDivs[this.select].className ="selectDiv";
}
Tab.prototype.getEle = function(){
//this指的实例
var oTab = document.getElementById(this.id);
this.oLis = oTab.getElementsByTagName("li");
this.oDivs = oTab.getElementsByTagName("div");
};
Tab.prototype.bind = function(){
for(var i = 0;i<this.oLis.length;i++){
//this不能跨作用查找,但是变量可以的
var that = this;//实例
(function(i){
that.oLis[i].onclick = function(){
that.changeTab(i);
}
})(i)
}
};
Tab.prototype.changeTab = function(n){
this.oLis[this.select].className ="";
this.oDivs[this.select].className = "";
this.oLis[n].className = "selectLi";
this.oDivs[n].className ="selectDiv";
this.select = n;
}
new Tab({id:"tab1"});
new Tab({id:"tab2",select:2})
</script>
----------
十秒倒计时
var count = 0;
var queryNumber = setInterval(function () {
count++;
if (count > 9) {
document.body.innerHTML = '结束!';
clearInterval(queryNumber);
return;
}
var areaStr = '0123456789',
result = '';
for (var i = 0; i < 4; i++) {
var n = Math.round(Math.random() * 9),
char = areaStr[n];
result += char;
}
document.body.innerHTML = result;
}, 1000);
utils()
var utils = (function () {
var isCompatible = 'getElementsByClassName' in document,
isSupportJSON = 'JSON' in window;
//=>toArray & toJSON
var toArray = function (classAry) {
var ary = [];
if (isCompatible) {
ary = Array.prototype.slice.call(classAry);
} else {
for (var i = 0; i < classAry.length; i++) {
ary[ary.length] = classAry[i];
}
}
return ary;
};
var toJSON = function (str) {
return isSupportJSON ? JSON.parse(str) : eval('(' + str + ')');
};
//=>offset & winBox
var offset = function (curEle) {
var l = curEle.offsetLeft,
t = curEle.offsetTop,
p = curEle.offsetParent;
while (p.tagName !== 'BODY') {
if (isCompatible === false && isSupportJSON === true) {
l += p.clientLeft;
t += p.clientTop;
}
l += p.offsetLeft;
t += p.offsetTop;
p = p.offsetParent;
}
return {left: l, top: t};
};
var winBox = function (attr, value) {
if (typeof value !== 'undefined') {
document.documentElement[attr] = value;
document.body[attr] = value;
return;
}
return document.documentElement[attr] || document.body[attr];
};
//=>children
function children(ele, attr) {
var ary = [];
isCompatible ? ary = toArray(ele.children) : ary = toArray(ele.childNodes);
for (var k = 0; k < ary.length; k++) {
var obj = ary[k];
if (obj.nodeType === 1) {
if (attr && attr.toLowerCase() !== obj.tagName.toLowerCase()) {
ary.splice(k, 1);
k--;
}
} else {
ary.splice(k, 1);
k--;
}
}
return ary;
}
//=>getElementsByClassName
function getElementsByClassName(classStr, context) {
if (arguments.length === 0) return [];
context = context || document;
if (isCompatible) {
return toArray(context.getElementsByClassName(classStr));
}
var eleList = toArray(context.getElementsByTagName("*"));
var classList = classStr.replace(/^ +| +$/g, "").split(/ +/);
for (var i = 0; i < classList.length; i++) {
var cur = classList[i];
var reg = new RegExp("(^| +)" + cur + "( +|$)");
for (var j = 0; j < eleList.length; j++) {
if (!reg.test(eleList[j].className)) {
eleList.splice(j, 1);
j--;
}
}
}
return eleList;
}
//=>css
function getCss(curEle, attr) {
var value = null, reg = null;
if (isCompatible) {
value = window.getComputedStyle(curEle, null)[attr];
} else {
if (attr === 'opacity') {
value = curEle.currentStyle['filter'];
reg = /^alpha\(opacity=(.+)\)$/i;
return reg.test(value) ? reg.exec(value)[1] / 100 : 1;
}
value = curEle.currentStyle[attr];
}
reg = /^-?\d+(.\d+)?(pt|px|rem|em)?$/i;
return reg.test(value) ? parseFloat(value) : value;
}
function setCss(curEle, attr, value) {
if (attr === 'opacity') {
curEle.style.opacity = value;
curEle.style.filter = 'alpha(opacity=' + value * 100 + ')';
return;
}
!isNaN(value) && !/(fontWeight|lineHeight|zoom|zIndex)/i.test(attr) ? value += 'px' : null;
curEle.style[attr] = value;
}
function setGroupCss(curEle, options) {
if (Object.prototype.toString.call(options) !== '[object Object]') return;
for (var attr in options) {
if (options.hasOwnProperty(attr)) {
setCss(curEle, attr, options[attr])
}
}
}
function css() {
var len = arguments.length,
type = Object.prototype.toString.call(arguments[1]),
fn = getCss;
len >= 3 ? fn = setCss : (len === 2 && type === '[object Object]' ? fn = setGroupCss : null)
return fn.apply(this, arguments);
}
return {
toArray: toArray,
toJSON: toJSON,
offset: offset,
winBox: winBox,
children: children,
getElementsByClassName: getElementsByClassName,
css: css
}
})();
京东倒计时
//=> HTML
<!-- 京东倒计时抢购 -->
<div class="time">
<a href="#">
京东秒杀<br/>距离本场活动结束的时间还有: <span class="timeBox" id="timeBox">00:10:00</span>
</a>
</div>
//=> JS
/* 倒计时抢购*/
function computed() {
var timeBox = document.getElementById('timeBox');
var curTime = new Date(); // 电脑当前的时间
var targetTime = new Date('2018/02/03 16:30'); //我们自己给定的活动结束的时间;
var areaTime = targetTime - curTime;
//console.log(areaTime); //150621 毫秒数
if (areaTime < 0) {
timeBox.innerHTML = '活动已经结束啦~';
window.clearInterval(timer);
return;
}
/*得到的毫秒数 算出小时*/
var hour = Math.floor(areaTime / (1000 * 60 * 60));
/*从剩余的毫秒数中 算出分钟*/
areaTime -= hour * 1000 * 60 * 60;
/*减去小时占的毫秒数 剩下再去算分钟占的毫秒数*/
var minutes = Math.floor(areaTime / (1000 * 60));
/*从剩余的毫秒数中 算出秒*/
areaTime -= minutes * 1000 * 60;
var seconds = Math.floor(areaTime / 1000);
/*补0的操作 只要小于10 就在前面补一个0*/
hour < 10 ? hour = '0' + hour : hour;
minutes < 10 ? minutes = '0' + minutes : minutes;
seconds < 10 ? seconds = '0' + seconds : seconds;
timeBox.innerHTML = hour + ':' + minutes + ':' + seconds;
}
computed();
var timer = window.setInterval(computed, 1000); //每隔1s执行一次函数
京东倒计时(服务器端计时)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
.timeBox {
margin: 20px auto;
width: 300px;
font-size: 20px;
height: 50px;
line-height: 50px;
text-align: center;
border: 1px dashed lightblue;
}
.timeBox span {
color: lightcoral;
font-weight: bolder;
}
</style>
</head>
<body>
<div class="timeBox">抢购倒计时:
<span>--:--:--</span>
</div>
<script>
let timeBox = document.querySelector('.timeBox'),
timeSpan = timeBox.querySelector('span');
let autoTimer = null;
let _serverTime = null;
//从服务器获取时间会存在一个问题;由于服务器端返回数据需要时间所以客户端拿到返回的服务器时间的时候已经过去一会了,导致获取的时间和真实的时间是由一定的误差的,这个误差越小越好,那么如何减少误差呢
//在Ajax为2的时候就从响应头获取信息,而不是等到更靠后的状态四
//请求方式设置为HEAD,至获取响应头信息即可,响应如果不需要
//即使我们向服务器发送一个不存在的请求地址,返回的是404状态吗,但是响应头信息中会存在服务器时间(不建议使用,不友好)
let queryTime = function () {
//第二次执行querytime
//用之前记录的全局变量值,需要手动累加一秒
if (_serverTime) {
_serverTime = new Date(_serverTime.getTime() + 1000);
return _serverTime;
}
//第一次向服务器发送请求
return new Promise(resolve => {
let xhr = new XMLHttpRequest;
xhr.open('get', 'json/data.json');
xhr.onreadystatechange = () => {
if (xhr.readyState === 2) {
let serverTime = new Date(xhr.getResponseHeader('date'));
_serverTime = serverTime;
resolve(serverTime);
}
};
xhr.send(null);
});
};
//计算倒计时
let computedTime = function () {
//一个客户端每一秒都向服务器发送一个新的请求,这样服务器就负载了
//解决方案:创建一个全局变量,记录第一次从后台获取的服务器时间
let promise = queryTime();
promise instanceof Promise ? promise.then(fn) : fn(promise);
function fn(serverTime) {
let nowTime = serverTime;
let tarTime = new Date('2018/06/03 18:00:00');
let diffTime = tarTime - nowTime;
if (diffTime >= 0) {
let hours = Math.floor(diffTime / (1000 * 60 * 60));
diffTime = diffTime - hours * 3600000;
let minutes = Math.floor(diffTime / (1000 * 60));
diffTime = diffTime - minutes * 60000;
let seconds = Math.floor(diffTime / 1000);
hours < 10 ? hours = '0' + hours : null;
minutes < 10 ? minutes = '0' + minutes : null;
seconds < 10 ? seconds = '0' + seconds : null;
timeSpan.innerHTML = `${hours}:${minutes}:${seconds}`;
return;
}
timeSpan.innerHTML = `--:--:--`;
clearInterval(autoTimer);
}
};
computedTime();
autoTimer = setInterval(computedTime, 1000);
</script>
</body>
</html>
封装节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="list">
<li>0</li>
<li>1</li>
<li id="li2">2</li>
<li>3</li>
</ul>
<script type="text/javascript">
var oUl=document.getElementById("list");
function getChild(ele){
//childNodes
var childs = ele.childNodes;//所有子节点
var ary = [];
for(var i = 0;i<childs.length;i++){
var cur = childs[i];
if(cur.nodeType==1){
ary[ary.length] = cur;
}
}
return ary;
}
console.log(getChild(oUl));//获取元素下的所有元素节点。
----------
//->获取ele的哥哥元素节点
var oLi2 = document.getElementById("li2");
function getPrev(ele){
//通过哥哥节点判断出是否是哥哥元素节点
var pre = ele.previousSibling;
//先判断是否存在,若存在则判断是否是元素节点,若不是则继续往上取哥哥节点,再判断是否存在,若存在.....
while(pre){
if(pre.nodeType == 1){
return pre;
}
pre = pre.previousSibling
}
return pre;
}
console.log(getPrev(oLi2));//获取哥哥节点
----------
// ->获取所有的哥哥元素节点
function prevAll(ele){
var ary = [];
var pre = getPrev(ele);//要么是哥哥元素节点,要么就是null
while(pre){
ary[ary.length] = pre;
pre = getPrev(pre)
}
return ary;
}
console.log(prevAll(oLi2));
----------
// //获取某个元素ele的索引
/*function getIndex(ele){
return prevAll(ele).length;
}*/
function getIndex(ele){
var n = 0;
var pre = getPrev(ele);
while(pre){
n++;
pre = getPrev(pre);
}
return n;
}
console.log(getIndex(oLi2));
</script>
</body>
</html>
封装replace
String.prototype.myReplace = function myReplace(reg, callback) {
//=>this:str
//=>默认REG肯定加G了,CALLBACK肯定传递函数了
let res = reg.exec(this),
_this = this;
while (res) {
//=>res:每一次EXEC捕获的结果(数组)
let returnV = callback(...res);//=>捕获一次执行一次回调函数,并且把通过EXEC捕获的数组展开,每一项都依次传递给回调函数(returnV:当前回调函数执行的返回结果,我们要拿这个结果替换字符串中当前大正则匹配的内容)
let v = res[0],
i = _this.indexOf(v);
_this = _this.substring(0, i) + returnV + _this.substring(v.length + i);
res = reg.exec(this);
}
return _this;
};
let str = 'my name is {0},i am {1} years old!',
ary = ['周啸天哈哈', '28'];
str = str.myReplace(/\{(\d+)\}/g, function (...arg) {
let index = arg[1];
return ary[index];
});
console.log(str);
动画animate
//=>UTILS操作CSS工具库
let utils = (function () {
//=>获取样式
let getCss = (ele, attr) => {
let val = null,
reg = /^-?\d+(\.\d+)?(px|rem|em)?$/;
if ('getComputedStyle' in window) {
val = window.getComputedStyle(ele)[attr];
if (reg.test(val)) {
val = parseFloat(val);
}
}
return val;
};
//=>设置样式
let setCss = (ele, attr, value) => {
if (!isNaN(value)) {
if (!/^(opacity|zIndex)$/.test(attr)) {
value += 'px';
}
}
ele['style'][attr] = value;
};
//=>批量设置样式
let setGroupCss = (ele, options) => {
for (let attr in options) {
if (options.hasOwnProperty(attr)) {
setCss(ele, attr, options[attr]);
}
}
};
//=>合并为一个
let css = (...arg) => {
let len = arg.length,
fn = getCss;
if (len >= 3) {
fn = setCss;
}
if (len === 2 && typeof arg[1] === 'object') {
fn = setGroupCss;
}
return fn(...arg);
};
//=>EACH:遍历对象、数组、类数组
let each = (obj, callback) => {
if ('length' in obj) {
for (let i = 0; i < obj.length; i++) {
let item = obj[i],
res = callback && callback.call(item, i, item);
if (res === false) {
break;
}
}
return;
}
for (let attr in obj) {
if (obj.hasOwnProperty(attr)) {
let item = obj[attr],
res = callback && callback.call(item, attr, item);
if (res === false) {
break;
}
}
}
};
return {css, each}
})();
//=>ANIMATE动画库
~function () {
//=>匀速运动公式
let effect = {
Linear: (t, b, c, d) => t / d * c + b
};
//=>开始运动
window.animate = function animate(ele, target, duration = 1000, callback) {
//=>参数处理(传递三个值,第三个值是函数,其实本身想要代表的意思:第三个是回调函数,总时间是默认值即可)
if (typeof duration === 'function') {
callback = duration;
duration = 1000;
}
//=>准备数据
let time = 0,
begin = {},
change = {};
utils.each(target, (key, value) => {
begin[key] = utils.css(ele, key);
change[key] = value - begin[key];
});
//=>设置新动画之前清除原有正在运行的动画
clearInterval(ele.animateTimer);
ele.animateTimer = setInterval(() => {
time += 17;
//->动画结束
if (time >= duration) {
clearInterval(ele.animateTimer);
utils.css(ele, target);
callback && callback.call(ele);
return;
}
//->获取每个方向的当前位置,并且给元素设置
utils.each(target, (key, value) => {
let cur = effect.Linear(time, begin[key], change[key], duration);
utils.css(ele, key, cur);
});
}, 17);
};
}();
封装轮播图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/轮播图.css">
</head>
<body>
<!--section.container#container>(div.wrapper>div>img[src='img/banner1.jpg'])+(ul.focus>li.active+li*3)+(a[href='javascript:;'].arrow*2)-->
<section class="container" id="container">
<div class="wrapper">
<!--<div class="slide"><img src="img/banner1.jpg" alt=""></div>-->
<!--<div class="slide"><img src="img/banner2.jpg" alt=""></div>-->
<!--<div class="slide"><img src="img/banner3.jpg" alt=""></div>-->
<!--<div class="slide"><img src="img/banner4.jpg" alt=""></div>-->
</div>
<ul class="focus">
<!--<li class="active"></li>-->
<!--<li></li>-->
<!--<li></li>-->
<!--<li></li>-->
</ul>
<a href="javascript:;" class="arrow arrowLeft"></a>
<a href="javascript:;" class="arrow arrowRight"></a>
</section>
<script src="jquery-1.11.3.min.js"></script>
<script src="js/animate.js"></script>
<script src="js/轮播练习1.js"></script>
</body>
</html>
.container{
position: relative;
margin: 20px auto;
width: 1000px;
height: 300px;
overflow: hidden;
}
.container .wrapper{
position: absolute;
top: 0;
left: 0;
width: 4000px;
height: 100%;
}
.container .wrapper .slide{
float: left;
width: 1000px;
height: 100%;
overflow: hidden;
}
.container .wrapper .slide img{
display: block;
width: 100%;
height: auto;
}
.container .focus{
position: absolute;
z-index: 999;
bottom:10px;
left: 50%;
transform: translateX(-50%);
/*//基于css3中的transform变形属性,在不固定宽度的情况下实现水平居中(translateX:让当前元素水平位移)*/
padding: 4px;
height: 12px;
font-size: 0;
background: rgba(0,0,0,.5);
border-radius: 10px;//让其值为盒子高度的一半这样长方形的盒子修改为椭圆
}
.container .focus li{
display:inline-block;
margin: 0 4px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #FFF;
cursor: pointer;
}
.container .focus li.active{
background: #E01D20;
}
.container .arrow{
display: none;
position: absolute;
top:50%;
margin-top:-22.5px;
width: 28px;
height: 45px;
background: url("../img/pre.png") no-repeat;
opacity:0.3;
}
.container .arrow:hover{
opacity: 1;
}
.container .arrow.arrowLeft{
left: 0;
background-position:0 0;
}
.container .arrow.arrowRight{
right:0;
background-position:-50px 0;
}
let bannerRender = (function () {
let container = document.querySelector('#container'),
wrapper = container.querySelector('.wrapper'),
focus = container.querySelector('.focus'),
arrowLeft = container.querySelector('.arrowLeft'),
arrowRight = container.querySelector('.arrowRight'),
slideList = null,
focusList = null;
let stepIndex=0;//stepIndex记录当前展示块的索引(步长0)
let autoTimer=null;//自动轮播的定时器
let interval=1000;//间隔多长时间自动切换一次
// autoMove控制轮播图的运动和切换
let autoMove=function autoMove() {
stepIndex++;
if(stepIndex>slideList.length-1){
//说明再往后切换没有了,现在展示的是克隆的第一张,此时我们我让wrapper立即回到真实第一张的位置(left=0)然后step-index=1(这样我们可以切换到第二张)
utils.css(wrapper,'left',0);
stepIndex=1
}
//基于自主封装的animate实现切换动画
animate(wrapper,{left:-stepIndex*1000},200);
// utils.css(wrapper,'left',-stepIndex*1000)
changeFocus()
};
//让焦点跟着轮播图切换而切换运动到克隆那一张时候也需要让第一个lI有选种样式
let changeFocus=function changeFocus() {
//当轮播图运动到最后一张,就是克隆的第一张,我们需要让第一个li索引0,有选中的样式。
let tempIndex=stepIndex;
tempIndex===slideList.length-1?tempIndex=0:null;
[].forEach.call(focusList,(item,index)=>{
item.className=index===tempIndex?'active':'';
})
};
//获取数据
let queryData = function queryData() {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('get', 'json/banner.json', true);//第三个参数可以不写,默认是true
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
let data = JSON.parse(xhr.responseText);
resolve(data);
}
};
xhr.send(null);
});
};
//绑定数据
let bindHTML = function bindHTML(data) {
let strSlide = ``,
strFocus = ``;
data.forEach((item, index) => {
//解构的时候如果当前返回的数据中没有img我们可以让其等于默认图片
let {img = 'img/banner1.jpg', desc = '把握未来,掌握先机'} = item;
strSlide += `<div class="slide"><img src="${img}" alt="${desc}"></div>`;
strFocus += ` <li class="${index === 0 ? 'active' : ''}"></li>`;
});
wrapper.innerHTML = strSlide;
focus.innerHTML = strFocus;
slideList = wrapper.querySelectorAll('.slide');
focusList = focus.querySelectorAll('li');
//把现有的第一张克隆一份放到容器的末尾,由于queryselectAll不存在dom映射,新增加一个原有集合中还是之前的slide所以我们需要重新获取一个
wrapper.appendChild(slideList[0].cloneNode(true));
slideList = wrapper.querySelectorAll('.slide');
utils.css(wrapper, 'width', slideList.length * 1000);
};
let handleContainer=function handleContainer() {
container.onmouseenter=function () {
clearInterval(autoTimer);
arrowLeft.style.display=arrowRight.style.display='block';
};
container.onmouseleave=function () {
autoTimer=setInterval(autoMove,interval);
arrowLeft.style.display=arrowRight.style.display='none';
}
};
let handleFocus=function handleFocus() {
[].forEach.call(focusList,(item,index)=>{
item.onclick=function () {
stepIndex=index;//点击的是谁就让step-index运动到哪(step-index和点击li的索引一致即可)
animate(wrapper,{left:-stepIndex*1000},200);
changeFocus();
}
})
};
let handleArrow=function handleArrow() {
arrowRight.onclick=autoMove;//点击右按钮和自动轮播是一样的。
arrowLeft.onclick=function () {
stepIndex--;
//如果索引减减小于零,说明当前已经是第一张,不能再向右运动了,此时我们让wrapper瞬间移动到最后一张(最后一张和第一张一模一样),在让其到倒数第二张即可
if(stepIndex<0){
utils.css(wrapper,'left',-(slideList.length-1)*1000);
stepIndex=slideList.length-2;
}
animate(wrapper,{left:-stepIndex*1000},200);
changeFocus();
}
};
return {
init: function () {
let promise = queryData();
promise.then(bindHTML).then(()=>{
//开启定时器驱动的自动轮播
autoTimer=setInterval(autoMove,interval)
}).then(()=>{
//左右按钮或者焦点切换
handleContainer();
handleFocus();
handleArrow();
});
}
}
})();
bannerRender.init();
放大镜
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
.container{
margin: 20px auto;
width: 650px;
}
.smallBox{
float: left;
position: relative;
width: 300px;
height: 300px;
overflow: hidden;
}
.smallBox img{
display: block;
width: 100%;
height: 100%;
}
.smallBox .mark{
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
z-index:10;
background: rgba(255,0,0,.3);
cursor: move;
}
.bigBox{
position: relative;
float: left;
display: none;
width: 350px;
height: 350px;
overflow: hidden;
}
.bigBox img{
position: absolute;
top: 0;
left: 0;
display: block;
/*width: 1050px;*/
height: 1050px;
}
</style>
</head>
<body>
<div class="container clearfix">
<div class="smallBox">
<img src="img/1.jpg" alt="">
<!--<div class="mark"></div>-->
</div>
<div class="bigBox">
<img src="img/2.jpg" alt="">
</div>
</div>
<script>
let container=document.querySelector('.container'),
smallBox=container.querySelector('.smallBox'),
bigBox=container.querySelector('.bigBox'),
bigImg=bigBox.querySelector('img');
let mark=null;
smallBox.onmouseenter=function () {
if(!mark){
mark=document.createElement('div');
mark.className='mark';
smallBox.appendChild(mark);
bigBox.style.display='block';
}
};
smallBox.onmousemove=function (ev) {
if(!mark)return;
//鼠标在盒子中间计算的left和top值
let curL=ev.pageX-smallBox.offsetLeft-mark.offsetWidth/2;
let curT=ev.pageY-smallBox.offsetTop-mark.offsetHeight/2;
//计算出来的值不能超过边界;
let minL=0,
minT=0,
maxL=smallBox.offsetWidth-mark.offsetWidth,
maxT=smallBox.offsetHeight-mark.offsetHeight;
curL=curL<minL?minL:(curL>maxL?maxL:curL);
curT=curT<minT?minT:(curT>maxT?maxT:curT);
mark.style.left=curL+'px';
mark.style.top=curT+'px';
bigImg.style.left=-curL*3.5+'px';
bigImg.style.top=-curT*3.5+'px';
};
smallBox.onmouseleave=function () {
if(mark){
this.removeChild(mark);
mark=null;
bigBox.style.display='none';
}
}
</script>
</body>
</html>
事件委托,菜单栏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../../reset.min.css">
<style>
.menuBox {
margin: 20px auto;
width: 700px;
height: 270px;
border: 1px solid #000;
}
.navBox {
float: left;
width: 200px;
background: lightblue;
}
.navBox ul li {
height: 30px;
line-height: 30px;
}
.navBox ul li a {
display: block;
padding: 0 10px;
height: 100%;
font-size: 14px;
color: #555;
}
.navBox ul li a:hover {
background: lightcoral;
}
.detailBox {
display: none;
float: left;
width: 500px;
height: 100%;
background: #EEE;
font-size: 20px;
}
</style>
</head>
<body>
<section class="menuBox">
<nav class="navBox">
<ul>
<li><a href="#" target="_blank">导航1</a></li>
<li><a href="#" target="_blank">导航2</a></li>
<li><a href="#" target="_blank">导航3</a></li>
<li><a href="#" target="_blank">导航4</a></li>
<li><a href="#" target="_blank">导航5</a></li>
<li><a href="#" target="_blank">导航6</a></li>
<li><a href="#" target="_blank">导航7</a></li>
<li><a href="#" target="_blank">导航8</a></li>
<li><a href="#" target="_blank">导航9</a></li>
</ul>
</nav>
<div class="detailBox">导航1对应的详情内容</div>
</section>
<script src="../../jquery-1.11.3.min.js"></script>
<script>
//=>基于事件委托给最外层的盒子的MOUSE-OVER绑定方法,这样不管操作后代元素中的谁的MOUSE-OVER,这个方法都会执行
let $detailBox = $('.detailBox');
$(document.body).on('mouseover', function (ev) {
let target = ev.target,
tag = target.tagName,
$target = $(target),
$pars = $target.parents();//=>获取当前事件源的祖先元素
//=>如果事件源是NAV-BOX中的A或者LI(让DETAIL-BOX显示)
let flag = $pars.filter('.navBox').length > 0 ? true : false;//=>TRUE祖先中包含NAV-BOX,FALSE则相反
if ((tag === 'A' || tag === 'LI') && flag) {
let val = $target.text().match(/\d+/);
$detailBox.css('display', 'block').html(`导航${val}对应的内容`);
return;
}
/*//=>如果事件源是DETAIL-BOX或者是它的后代元素,不做处理
if ($target.hasClass('detailBox') || $pars.filter('.detailBox').length > 0) {
return;
}*/
$detailBox.css('display', 'none');
});
$detailBox.on('mouseover', function (ev) {
ev.stopPropagation();
});
</script>
</body>
</html>
鼠标跟随
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../../reset.min.css">
<style>
.container {
position: relative;
margin: 20px auto;
width: 462px;
height: 77px;
}
.container .imgBox li {
float: left;
margin-right: 18px;
width: 100px;
height: 75px;
border: 1px solid #000;
}
.container .imgBox li:nth-last-child(1) {
margin-right: 0;
}
.container .imgBox li img {
display: block;
width: 100%;
height: 100%;
}
.container .mark {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 400px;
height: 300px;
border: 1px solid #000;
}
.container .mark img {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<section class="container">
<ul class="imgBox clearfix">
<!--如果小图和大图在名字上没有固定的规则,我们把大图地址都以自定义属性的方式存储起来后其需要展示大图的时候,从自定义属性获取即可-->
<li><img src="img/apple_1.jpg" data-big='img/apple_1_bigger.jpg' alt=""></li>
<li><img src="img/apple_2.jpg" alt=""></li>
<li><img src="img/apple_3.jpg" alt=""></li>
<li><img src="img/apple_4.jpg" alt=""></li>
</ul>
<!--<div class="mark">-->
<!--<img src="img/apple_1_bigger.jpg" alt="">-->
<!--</div>-->
<!--如果有固定的规则我们完全可以不采用自定义属性的方式,而是基于规则自己处理和匹配即可-->
</section>
<script src="../../jquery-1.11.3.min.js"></script>
<!--<script>
//方案一:每一个li中都有一个大盒子,大盒子中存放的是大图,开始是隐藏的,鼠标进入到li中让其显示,并且让他的位置跟随鼠标的位置改变即可,鼠标离开li,让自己的大盒子消失即可(类似于放大镜small-box中出现mark)
//方案二:只有一个大盒子(可以在js中动态创建,也可以事先写好,控制显示隐藏),鼠标进入任意一个li,都让大盒子显示,并且大盒子中存放的图片根据当前进入的li动态改变,同样实现鼠标移动,让大盒子也跟着移动。
<!–let $container=$('.container'),
$imgList=$('.container>.imgBox>li'),
$mark=null;
$imgList.on('mouseover',function (ev) {
//创建Mark根据经过的li的小图片动态管控Mark中的大图片
let srcStr=$(this).children('img').attr('src');
srcstr=$srcStr.replace(/_(\d+)/g,'_$1_bigger');
if(!$mark){
$mark=$(`<div class="mark">
<img src="${srcStr}" alt="">
</div>`);
$container.append($mark);
}
}).on('mouseout',function (ev) {
//移除Mark
if($mark){
$mark.remove();
$mark=null;
}
}).on('mousemove',function (ev) {
//根据鼠标的位置计算出Mark的位置
let{top:conTop,left:conLeft}=$container.offset();
curL = ev.pageX - conLeft + 20;
curT = ev.pageY - conTop + 20;
$mark.css({
top: curT,
left: curL
});
})
</script>–>-->
<script>
let $container = $('.container'),
$imgList = $('.container>.imgBox>li'),
$mark = null;
$imgList.on('mouseover', function (ev) {
//=>创建MARK:根据经过的LI中的小图片,动态管控MARK中的大图片
let srcStr = $(this).children('img').attr('src');
srcStr = srcStr.replace(/_(\d+)/g, '_$1_bigger');
if (!$mark) {
$mark = $(`<div class="mark">
<img src="${srcStr}" alt="">
</div>`);
$container.append($mark);
}
}).on('mouseout', function (ev) {
//=>移除MARK
if ($mark) {
$mark.remove();
$mark = null;
}
}).on('mousemove', function (ev) {
//=>根据鼠标的位置计算出MARK的位置
let {top: conTop, left: conLeft} = $container.offset(),
curL = ev.pageX - conLeft + 20,
curT = ev.pageY - conTop + 20;
$mark.css({
top: curT,
left: curL
});
});
</script>
</body>
</html>
多层菜单树
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../../reset.min.css">
<style type="text/css">
html, body {
height: 100%;
overflow: hidden;
}
.menuBox {
width: 300px;
height: 100%;
overflow: auto;
/*CSS3中新增的背景颜色渐变*/
background: -webkit-linear-gradient(top left, lightblue, lightcyan, lightpink, lightgoldenrodyellow);
}
.menuBox li {
position: relative;
}
.menuBox li span {
margin-left: 20px;
line-height: 30px;
font-size: 14px;
}
.menuBox li em {
position: absolute;
left: 0;
top: 7px;
width: 16px;
height: 16px;
background: url("img1/icon.png") no-repeat;
}
.menuBox li em.plus {
background-position: -59px -28px;
}
.menuBox li em.minus {
background-position: -42px -29px;
}
.menuBox .level1 {
margin-left: 10px;
}
.menuBox .level2 {
margin-left: 20px;
}
.menuBox .level3 {
margin-left: 30px;
}
.menuBox .level4 {
margin-left: 40px;
}
/*CSS3中的NOT伪类:除了XXX*/
.menuBox ul:not(.level1) {
display: none;
}
</style>
</head>
<body>
<!--
事件委托:
一个容器很多后代元素的点击行为都要处理一些事情
之前的思路是把需要操作的元素一一获取,然后再一一绑定,在不停的方法中完成不同的需求
现在是基于事件的冒泡传播,我们可以只给容器的click绑定一个方法,这样不管以后点击的是容器中的哪一个后代元素,都会通过冒泡传播机制,把容器的click行为触发,把绑定的方法执行,我们在方法执行的时候,根据事件对象中事件源ev.target来做不同的业务处理即可,这种机制即是事件委托机制
1.容器中很多后代元素的某个行为都要进行操作,委托给容器处理是不错的选择
2.元素是动态绑定的
3.需求是除了某某某,剩下的操作都是干同样的事情。此时把点击行为的操作委托给body,事件源是某某做什么,不是,统一做什么
-->
<section class="menuBox">
<ul class="level1">
<li>
<!--每个li中必有一个span,如果有下一级,再有em和ul即可-->
<!--plus:+ minus:- -->
<em class="plus"></em><span>产品技术部门</span>
<ul class="level2">
<li><span>产品小组</span></li>
<li>
<em class="plus"></em><span>UI小组</span>
<ul class="level3">
<li><span>UI设计师</span></li>
<li><span>UE体验师</span></li>
</ul>
</li>
<li>
<em class="plus"></em><span>开发小组</span>
<ul class="level3">
<li>
<em class="plus"></em><span>前端开发</span>
<ul class="level4">
<li><span>PC设备开发</span></li>
<li><span>移动设备开发</span></li>
<li><span>VR/AI开发</span></li>
<li><span>NATIVE-APP开发</span></li>
<li><span>NODE开发</span></li>
</ul>
</li>
<li><span>后台开发</span></li>
<li><span>服务器开发</span></li>
<li><span>公共技术研发团队</span></li>
</ul>
</li>
<li><span>测试小组</span></li>
<li><span>运维小组</span></li>
</ul>
</li>
<li>
<!--每个li中必有一个span,如果有下一级,再有em和ul即可-->
<!--plus:+ minus:- -->
<em class="plus"></em><span>产品技术部门</span>
<ul class="level2">
<li><span>产品小组</span></li>
<li>
<em class="plus"></em><span>UI小组</span>
<ul class="level3">
<li><span>UI设计师</span></li>
<li><span>UE体验师</span></li>
</ul>
</li>
<li>
<em class="plus"></em><span>开发小组</span>
<ul class="level3">
<li>
<em class="plus"></em><span>前端开发</span>
<ul class="level4">
<li><span>PC设备开发</span></li>
<li><span>移动设备开发</span></li>
<li><span>VR/AI开发</span></li>
<li><span>NATIVE-APP开发</span></li>
<li><span>NODE开发</span></li>
</ul>
</li>
<li><span>后台开发</span></li>
<li><span>服务器开发</span></li>
<li><span>公共技术研发团队</span></li>
</ul>
</li>
<li><span>测试小组</span></li>
<li><span>运维小组</span></li>
</ul>
</li>
<li>
<!--每个li中必有一个span,如果有下一级,再有em和ul即可-->
<!--plus:+ minus:- -->
<em class="plus"></em><span>产品技术部门</span>
<ul class="level2">
<li><span>产品小组</span></li>
<li>
<em class="plus"></em><span>UI小组</span>
<ul class="level3">
<li><span>UI设计师</span></li>
<li><span>UE体验师</span></li>
</ul>
</li>
<li>
<em class="plus"></em><span>开发小组</span>
<ul class="level3">
<li>
<em class="plus"></em><span>前端开发</span>
<ul class="level4">
<li><span>PC设备开发</span></li>
<li><span>移动设备开发</span></li>
<li><span>VR/AI开发</span></li>
<li><span>NATIVE-APP开发</span></li>
<li><span>NODE开发</span></li>
</ul>
</li>
<li><span>后台开发</span></li>
<li><span>服务器开发</span></li>
<li><span>公共技术研发团队</span></li>
</ul>
</li>
<li><span>测试小组</span></li>
<li><span>运维小组</span></li>
</ul>
</li>
</ul>
</section>
<script src="../../jquery-1.11.3.min.js"></script>
<script>
let $menuBox = $('.menuBox');
$menuBox.on('click', function (ev) {
// alert('ok');
let target = ev.target,
$target = $(target),
tarTag = target.tagName;
//合并事件源:点击的是em,我们让target也等于它弟弟span此时target只有span我们才处理,统一基于span位置为参照物即可。
if (tarTag === 'EM') {
$target = $target.next();
target = $target[0];
tarTag = target.tagName;
}
if (tarTag === "SPAN") {
let $ul = $target.next('ul'),
$em = $target.prev('em');
//基于jq获取的结果一般都是jq对象,即使没有获取到元素也是一个length为零的空对象,而不是null,所以if($ul){}这样算存在,是不行的,(如果没有下级结构我们什么都不做处理,有下一级结构在控制显示和隐藏即可)
if ($ul.length === 0) return;
let promise = new Promise(resolve => {
$ul.stop().slideToggle(200, function () {
resolve();
});
});
//em的样式类名:如果是plus,说明当前是折叠的,我们应当让其展开反之让其折叠起来
if ($em.hasClass('plus')) {
$em.addClass('minus').removeClass('plus')
} else {
$em.addClass('plus').removeClass('minus')
}
promise.then(()=>{
$ul.find('em').removeClass('minus').addClass('plus');
$ul.find('ul').css('display','none');
});
}
})
</script>
</body>
</html>
模态框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="../../reset.min.css">
<style type="text/css">
html, body {
height: 500%;
-webkit-user-select: none;
}
.dialogMark {
position: fixed;
top: 0;
left: 0;
z-index: 100;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .3);
}
.diaLogBox {
/*控制盒子在中间我们最好在js中实现,经过精密的计算,计算出具体的top和left而不是模糊的百分比这样我们在js中通过*/
width: 200px;
position: fixed;
height: 300px;
background: #FFF;
z-index: 101;
overflow: hidden;
}
.diaLogBox h3 {
position: relative;
padding: 0 10px;
height: 50px;
line-height: 50px;
background: #DDD;
border-bottom: 1px solid #aaaaaa;
cursor: move;
}
.diaLogBox h3 i {
position: absolute;
right: 10px;
top: 50%;
margin-top: -10px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
font-size: 16px;
color: #E01D20;
font-style: normal;
font-family: "Microsoft JhengHei";
background: lightgoldenrodyellow;
cursor: pointer;
}
</style>
</head>
<body>
<!--dialog:模态框-->
<div class="dialogMark"></div>
<div class="diaLogBox" id="diaLogBox">
<h3 class="title">大力出奇迹
<i class="closeBtn">X</i>
</h3>
<div class="con">
</div>
</div>
<script>
let dialogRender = (function () {
let dialogMark = document.getElementsByTagName('dialogMark')[0];
let diaLogBox = document.getElementById('diaLogBox');
let title = diaLogBox.getElementsByTagName('h3')[0];
let closeBtn=diaLogBox.getElementsByTagName('i')[0];
let winW = document.documentElement.clientWidth || document.body.clientWidth;
let winH = document.documentElement.clientHeight || document.body.clientHeight;
let maxL = winW - diaLogBox.offsetWidth;
let maxT = winH - diaLogBox.offsetHeight;
let Down = function (ev) {
console.log(1);
ev = ev || window.event;
//记录鼠标起始位置&&盒子的起始位置
this.strX = ev.clientX;
this.strY = ev.clientY;
this.strL = diaLogBox.offsetLeft;
this.strT = diaLogBox.offsetTop;
//谷歌下不兼容,用setCapture,releaseCapture解决鼠标焦点丢失问题
if(this.setCapture){
this.onmousemove=Move;
this.onmouseup=Up;
this.setCapture();
return;
}
//按下代表拖拽开始,会有鼠标丢失的问题,
// this.onmousemove=Move;
// this.onmouseup=Up;
//解决方法:
//用document解决鼠标焦点丢失问题
//let _this = this;
document.onmousemove = (ev) => {
Move.call(this,ev);
};
document.onmouseup = (ev) => {
Up.call(this,ev);
}
};
let Move = function (ev) {
// console.log(this);
ev = ev || window.event;
let curL = ev.clientX - this.strX + this.strL;
let curT = ev.clientY - this.strY + this.strT;
console.log(curT, ev.clientY, this.strY, this.strT);
//边界处理
curL = curL < 0 ? 0 : (curL > maxL ? maxL : curL);
curT = curT < 0 ? 0 : (curT > maxT ? maxT : curT);
console.log(curT);
diaLogBox.style.left = curL + 'px';
diaLogBox.style.top = curT + 'px';
console.log(diaLogBox.style.top);
};
let Up = function (ev) {
//谷歌下不兼容,用setCapture,releaseCapture解决鼠标焦点丢失问题
if(this.setCapture){
this.onmousemove=null;
this.onmouseup=null;
this.releaseCapture();
return;
}
//用document解决鼠标焦点丢失问题
document.onmousemove = null;
document.onmouseup = null;
};
closeBtn.onclick=function () {
diaLogBox.style.display='none';
};
return {
init: function () {
//让盒子处于页面中间
diaLogBox.style.left = maxL / 2 + 'px';
diaLogBox.style.top = maxT / 2 + 'px';
title.onmousedown = Down;
}
}
})();
dialogRender.init();
</script>
</body>
</html>
弹性拖拽
~function anonymouse(window) {
class Subscribe{
constructor(){
//创建一个实例,每一个实例都有一个自己独有的容器,管理自己需要执行的方法即可
this.pond=[];
}
//向计划表(池子中)增加方法:去重
//fn:我们需要增加的方法
add(fn){
let pond= this.pond;
let isExist=false;
pond.forEach(item=>{
item===fn?isExist=true:null;
});
!isExist?pond.push(fn):null;
//pond.push(fn);
}
//向计划表(池子中)移除方法
//fn:我们需要移除的方法
remove(fn){
let pond= this.pond;
pond.forEach((item,index)=>{
if(item===fn){
// pond.splice(index,1);//此时不能基于slice删除,因为这种删除方法会改变原有数组,例如通知方法执行,当执行到fn3的时候,fire索引是2,但是基于splice把fn1,fn2删除后,原始数组后面的项都向前提前两位,此时fire中继续遍历下一个方法索引3,已经找不到和他匹配的那一项了.
//就是数组塌陷,改变了后面的索引,导致fire不能找到
//让当前项赋值为null,这样函数移除掉了,但是此时的数组结构没有改变,不会出现数组塌陷的问题
pond[index]=null;//item=null是不行的
}
})
}
//如果传递参数信息了,把这些参数依次赋值给执行的每一个方法
fire(...arg){
let pond=this.pond;
//remove机制处理了此时的item不一定是函数了,还有可能是null,null的话不执行,而且最好是把这一项删除掉
for (let i = 0; i < pond.length; i++) {
let item = pond[i];
if(item===null){
pond.splice(i,1);
i--;
continue;
}
item(...arg);
}
}
}
window.Subscribe=Subscribe;
}(window);
let subscribe=new Subscribe();
// subscribe.add();
// subscribe.remove();
// subscribe.fire();
let fn1=function (x,y) {
console.log(1,x,y);
};
let fn2=function () {
console.log(2);
};
let fn3=function () {
console.log(3);
subscribe.remove(fn1);
subscribe.remove(fn2);
};
let fn4=function () {
console.log(4);
};
subscribe.add(fn1);
subscribe.add(fn2);
subscribe.add(fn3);
subscribe.add(fn1);
subscribe.add(fn4);
setTimeout(()=>{
subscribe.fire(10,20)
});
封装AJAX
;(function anonymous(window) {
//=>CREATE AJAX CLASS
function ajax(options) {
return new init(options);
}
//=>AJAX PROTOTYPE
ajax.prototype = {
constructor: ajax,
check() {
return this.url.indexOf('?') > -1 ? '&' : '?';
},
handleDataType(xhr) {
let dataType = this.dataType.toUpperCase(),
result = xhr.responseText;
switch (dataType) {
case 'TEXT':
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
break;
}
return result;
},
handleData() {
/*
* 1.如果DATA传递的是一个对象,我们需要把它转换为X-WWW-FORM-URLENCODED这种字符串的格式(客户端传递给服务器端的内容一般都是这种格式,除此之外还有RAW等)
* 2.如果是GET请求,我们需要把第一步解析后的结果放到URL的末尾,基于“问号传参传递过去”(如果是POST不需要管)
*/
let {method, data} = this;
if (!data) return;
if (typeof data === 'object') {
let str = ``;
for (let attr in data) {
if (data.hasOwnProperty(attr)) {
str += `${attr}=${data[attr]}&`;
}
}
str = str.substring(0, str.length - 1);
this.data = data = str;
}
if (_regGET.test(method)) {
this.url += `${this.check()}${data}`;
this.data = null;
}
},
handleCache() {
let {method, cache} = this;
if (cache === false && _regGET.test(method)) {
this.url += `${this.check()}_=${+(new Date())}`;
}
},
getServerTime() {
//...
},
send() {
this.handleData();
this.handleCache();
//=>AJAX的四步操作
let {method, url, async, data, success, error} = this,
xhr = new XMLHttpRequest;
xhr.open(method, url, async);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (/^(2|3)\d{2}$/.test(xhr.status)) {
//=>成功
let result = this.handleDataType(xhr);
success.call(this, result, xhr);
} else {
//=>失败
error.call(this, xhr.statusText, xhr);
}
}
};
xhr.send(data);
}
};
//=>INIT
let _emptyFn = new Function(''),
_regGET = /^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i,
_default = {
url: '',
method: 'GET',
data: null,
dataType: 'JSON',
async: true,
cache: true,
success: _emptyFn,
error: _emptyFn
};
function init(options) {
//=>THIS:INIT的实例(相当于AJAX的实例)
options = {..._default, ...options};//=>Object.assign(_default,options) ES6中新增的合并对象的方法
for (let attr in options) {
if (!options.hasOwnProperty(attr)) break;
this[attr] = options[attr];
}
//=>SEND AJAX
this.send();
}
//=>让INIT的实例等价于AJAX的实例
init.prototype = ajax.prototype;
window.ajax = ajax;
})(window);
promise 封装AJAX
~(function anonymous(window) {
//=>设置默认的参数配置项
let _default = {
method: 'GET',
url: '',
baseURL: '',
headers: {},
dataType: 'JSON',
data: null,//=>POST系列请求基于请求主体传递给服务器的内容
params: null,//=>GET系列请求基于问号传参传递给服务器的内容
cache: true
};
//=>基于PROMISE设计模式管理AJAX请求
let ajaxPromise = function ajaxPromise(options) {
//=>OPTIONS中融合了:默认配置信息、用户基于DEFAULTS修改的信息、用户执行GET/POST方法时候传递的配置信息,越靠后的优先级越高
let {url, baseURL, method, data, dataType, headers, cache, params} = options;
//=>把传递的参数进一步进行处理
if (/^(GET|DELETE|HEAD|OPTIONS)$/i.test(method)) {
//=>GET系列
if (params) {
url += `${ajaxPromise.check(url)}${ajaxPromise.formatData(params)}`;
}
if (cache === false) {
url += `${ajaxPromise.check(url)}_=${+(new Date())}`;
}
data = null;//=>GET系列请求主体就是什么都不放
} else {
//=>POST系列
if (data) {
data = ajaxPromise.formatData(data);
}
}
//=>基于PROMISE发送AJAX
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest;
xhr.open(method, `${baseURL}${url}`);
//=>如果HEADERS存在,我们需要设置请求头
if (headers !== null && typeof headers === 'object') {
for (let attr in headers) {
if (headers.hasOwnProperty(attr)) {
let val = headers[attr];
if (/[\u4e00-\u9fa5]/.test(val)) {
//=>VAL中包含中文:我们把它进行编码
//encodeURIComponent/decodeURIComponent
val = encodeURIComponent(val);
}
xhr.setRequestHeader(attr, val);
}
}
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (/^(2|3)\d{2}$/.test(xhr.status)) {
let result = xhr.responseText;
dataType = dataType.toUpperCase();
dataType === 'JSON' ? result = JSON.parse(result) : (dataType === 'XML' ? result = xhr.responseXML : null);
resolve(result);
return;
}
reject(xhr.statusText);
}
};
xhr.send(data);
});
};
//=>把默认配置暴露出去,后期用户在使用的时候可以自己设置一些基础的默认值(发送AJAX请求的时候按照用户配置的信息进行处理)
ajaxPromise.defaults = _default;
//=>把对象转换为URLENCODED格式的字符串
ajaxPromise.formatData = function formatData(obj) {
let str = ``;
for (let attr in obj) {
if (obj.hasOwnProperty(attr)) {
str += `${attr}=${obj[attr]}&`;
}
}
return str.substring(0, str.length - 1);
};
ajaxPromise.check = function check(url) {
return url.indexOf('?') > -1 ? '&' : '?';
};
//=>GET
['get', 'delete', 'head', 'options'].forEach(item => {
ajaxPromise[item] = function anonymous(url, options = {}) {
options = {
..._default,//=>默认值或者基于defaults修改的值
...options,//=>用户调取方法传递的配置项
url: url,//=>请求的URL地址(第一个参数:默认配置项和传递的配置项中都不会出现URL,只能这样获取)
method: item.toUpperCase()//=>以后执行肯定是ajaxPromise.head执行,不会设置METHODS这个配置项,我们自己需要配置才可以
};
return ajaxPromise(options);
};
});
//=>POST
['post', 'put', 'patch'].forEach(item => {
ajaxPromise[item] = function anonymous(url, data = {}, options = {}) {
options = {
..._default,
...options,
url: url,
method: item.toUpperCase(),
data: data
};
return ajaxPromise(options);
};
});
window.ajaxPromise = ajaxPromise;
})(window);
简易双向数据绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" id="input">
<script>
let obj = {};
let temp= {};
Object.defineProperty(obj, "name", {
/* configurable:false,//是否可配置,是否可删除
writable:false,//是否可重写,重新符值
enumerable:false,//是否可枚举*/
get() {//取obj.name属性会触发
return temp['name']
},
set(val) {//给obj赋值会触发get方法
temp['name']=val;//改变temp的结果
input.value=obj.name;//将值赋予输入框
},
});
input.value=obj.name;//页面一加载会调用get方法
input.addEventListener('input',function () {//等待输入框的变化
obj.name=this.value;//当值变化时,会调用set方法
})
</script>
</body>
</html>
----------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>201803JS</title>
</head>
<body>
<div id="app">
<input type="text" v-model="name">
<!--<input type="text" v-model="name">-->
<!--<input type="text" v-model="age">-->
<p>{{name}} </p>
<!--<p>{{age}}</p>-->
</div>
</body>
</html>
<script>
let data={name:"珠峰",age:10};
let app=document.getElementById("app");
let inputs=app.getElementsByTagName("input");
//获取app下面除了input之外的所有子元素节点
let nodeList=[...app.children].filter(item=>item.nodeName!="INPUT");
let cloneList=nodeList.map(item=>item.cloneNode(true));
for(let item of inputs){
if(item.getAttribute("v-model")){
item.value=data[item.getAttribute("v-model")];
}
item.oninput=function () {
data[this.getAttribute("v-model")]=this.value;
}
}
let reg=/\{\{(\w+)}}/;
nodeList.forEach((item)=>{
if(reg.test(item.innerHTML)){
item.innerHTML=item.innerHTML.replace(reg,(...arg)=>data[arg[1]])
}
});
Object.defineProperties(data,{
name:{
set(val){
//console.log(this==data);true
for(let item of inputs){
if(item.getAttribute("v-model")=="name"){
item.value=val;
}
}
cloneList.forEach((item,index)=>{
nodeList[index].innerHTML=item.innerHTML.replace(/\{\{name}}/g,()=>val)
})
},
get(){
}
},
// age:{}
});
data.name="mq";
console.log(data);
</script>
京东购物车
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="css/bootstrap.css">
</head>
<body>
<div id="app">
<!--default灰色 success绿色 danger红色warning警告色info浅蓝色primary蓝色-->
<div class="container"><!--居中-->
<div class="row"><!--每一行有12列-->
<table class="table table-bordered table-striped table-hover"><!--带边框,条纹表格,鼠标悬停-->
<caption style="caption-side: top" class="h1 text-danger text-center">购物车</caption><!--标题-->
<tr>
<th>全选<input type="checkbox" v-model="isAll"></th>
<th>商品</th>
<th>单价</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in products">
<td><input type="checkbox" v-model="item.isSelected"></td>
<!--v-bind:或者: 叫动态绑定数据 图片-->
<td>
<img :src="item.productCover" :title="item.productName" alt=""><span>{{item.productName}}</span>
</td>
<!--价格-->
<td>
¥{{item.productPrice}}
</td>
<!--数量-->
<td>
<input type="number" min="0" v-model="item.productCount">
</td>
<!--价格-->
<td>
¥{{item.productPrice*item.productCount|toFixed}}
</td>
<!--删除-->
<td>
<button class="btn btn-danger" @click="remove(item)">删除</button>
</td>
</tr>
<!--汇总价格-->
<tr>
<!-- colspan='6'合并单元格-->
<td colspan="6">总价格¥{{sum|toFixed}}</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<script>
let vm = new Vue({
el: '#app',
created() {
//生命周期函数,this是当前实例vm,只要数据初始化完成就会出发
//一般请求数据的时候在这里请求
this.getData();
},
data: {
products: [],
},
methods: {
//写在这里的函数使用的时候都是通过this.方法()语法糖async await
async getData() {
this.products = (await axios.get('data/carts.json')).data;
},
remove(cur) {/*删除按钮,cur是当前点击的这一项*/
this.products= this.products.filter(item=>item!==cur);
},
},
filters: {
toFixed(target) {
return Number(target).toFixed(2);
}
} ,
computed: {//当给全选赋值时要影响其他人的变化,当页面刷新时获取全选值,是根据下面的checkbox计算出来的结果给全选赋值
isAll: {//放在computed最后也会放在vm上不能和methods与data重名
get() {//get和set this指向实例 默认v-model会获取isAll的值,所以会调用get方法,every是遍历的每一项都为ture才返回true
return this.products.every(item => item.isSelected)
},
set(val) {//设置值,让products中的每一项item中的isSelected都为设置的值。
this.products.forEach(item => item.isSelected = val)
}
},
sum(){//当只有get方法的时候可以直接写成sum(){},正常写法是sum:{get(){}}
return this.products.reduce((prev,next)=>{
if(!next.isSelected)return prev;//如果当前的值是false(就是选框为选中),则返回上一个的值。相当于缓存结果
return prev+next.productPrice*next.productCount;
},0);
},
}
})
</script>
树菜单栏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
ul,li{
list-style: none;
}
</style>
</head>
<body>
<div id="app">
<ul v-for="(item,index) in tree " ><!--循环数据tree的每一项item-->
<li @click.self="item.isShow=!item.isShow">
<input type="checkbox" v-model="item.is" @change="checkAll(item)">{{item.name}}
</li>
<ul v-show="!item.isShow"><!--循环item中的list的每一项数字num-->
<li v-for="num in item.list">
<input type="checkbox" v-model="item.listSelect" :value='num' @change="checkOne(item)">{{num}}
</li>
{{item.listSelect}}
</ul>
</ul>
</div>
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<script>
let vm=new Vue({
el:("#app"),
data:{
tree:[]
},
created(){
this.queryData();
},
methods:{
async queryData(){
this.tree= (await axios.get('data/list.json')).data;
},
checkAll(item){
item.is ? item.listSelect = item.list : item.listSelect = [];
},
checkOne(item){
item.is = item.listSelect.length == item.list.length;
}
},
})
</script>
</body>
</html>