5、数据相加赋值相等克隆、字符串|数组|对象之为空|含某项|项位置|位置项|遍历|相似、字符串|正则|数组|对象之方法、递归(虚拟DOM、快速排序、react框架)、两行布局、两列布局、三列布局、五种盒子居中、em与rem自适应、Flex布局、多图片延迟加载下载、canvas绘图之党徽|五角星|时钟、弹窗之内置|拖拽缩放、表格排序、拖拽之普通|zTree(2900行)

一、数据相加赋值相等克隆
 来源,https://blog.csdn.net/yyh1994/article/details/129316857
1、小数相加出错
  (1)现象,console.log( 0.1+0.2 ); //0.30000000000000004,3和4之间有15个0
  (2)原因
    A、人用十进制运算
    B、计算机把人的十进制转化成自身的二进制,然后进行运算
    C、在JS中,遵循IEEE754标准(有3个E),使用双精度浮点数(double)64位(8字节)来存储一个浮点数
  (3)十进制转化为二进制--整数 
    A、十进制数除以2,得到商和余数,余数为二进制数的最低位
    B、商除以2,得到商和余数,余数为二进制数的次低位
    C、以此类推,直到商为0时为止,0为二进制数的最高位
  (4)十进制转化为二进制--小数
    A、将十进制小数乘以2,取出结果的整数部分,为小数部分的最高位
    B、将得到的小数部分再次乘以2,取出结果的整数部分,为小数部分的次高位
    C、直到小数部分为零或者达到所需的精度
  (5)将0.875转换为二进制小数,过程如下
    A、0.875乘以2得到1.75,取整数部分1,为小数部分的最高位
    B、0.75乘以2得到1.5,取整数部分1,为小数部分的次高位
    C、0.5乘以2得到1.0,取整数部分1
    D、0.875的二进制表示为0.1110
2、i=i+1,编程理解与数学理解
  (1)编程理解,赋值,把右边的计算结果赋值给左边的变量     
  (2)数学理解,移项,合并同类项0=1      
3、数据相等的判断方法
  // 封装一个判断两个js数据是否相等的方法
  //(1)方法1
  function isDeepEqualA(aaa, bbb) { 
    if (typeof aaa !== typeof bbb || Array.isArray(aaa) !== Array.isArray(bbb)) { 
      //以下,A、数据类型不同;B、数据类型相同,且1个是数组,1个不是数组(是对象)
      return false;
    } else if (Array.isArray(aaa)) { //以下,2个同为数组
      for (let i = 0; i < aaa.length; i++) {
        if (!isDeepEqualA(aaa[i], bbb[i])) {
          return false;
        }
      }
    } else if (typeof aaa === 'object' && aaa !== null && bbb !== null) { //以下,2个都为对象(都不为null)
      const aaaKeys = Object.keys(aaa).sort();
      const bbbKeys = Object.keys(bbb).sort();
      if (JSON.stringify(aaaKeys) !== JSON.stringify(bbbKeys)) {
        return false;
      }
      for (let key of aaaKeys) {
        if (!isDeepEqualA(aaa[key], bbb[key])) {
          return false;
        }
      }
    } else { //以下,A、2个同为基本类型;B、2个同为对象,且1个为null,1个(不)为null
      return aaa === bbb;
    }
    return true;
  }
  //(2)方法2
  function isDeepEqualB(aaa, bbb) {
    if (Object.prototype.toString.call(aaa) !== Object.prototype.toString.call(bbb)) { //以下,2个不同类型的数据
      return false;
    } else if (Object.prototype.toString.call(aaa) === "[object Array]") { //以下,2个同为数组类型的数据
      for (let i = 0; i < aaa.length; i++) {
        if (!isDeepEqualB(aaa[i], bbb[i])) {
          return false;
        }
      }
    } else if (Object.prototype.toString.call(aaa) === "[object Object]") { //以下,2个同为对象类型的数据
      const aaaKeys = Object.keys(aaa).sort();
      const bbbKeys = Object.keys(bbb).sort();
      if (JSON.stringify(aaaKeys) !== JSON.stringify(bbbKeys)) {
        return false;
      }
      for (let key of aaaKeys) {
        if (!isDeepEqualB(aaa[key], bbb[key])) {
          return false;
        }
      }
    } else { //以下,2个同为基本类型或同为null的数据
      return aaa === bbb;
    }
    return true;
  }
  //(3)数据
  if(1 == 1){
    console.log( '正在使用A组数据' );
    var aaa = [ 0,  1,  2,  3 ];
    var bbb = { 0:0, 1:1, 2:2, 3:3 };
  }else{
    console.log( '正在使用B组数据' );
    var aaa = { a: 1, b: 'b', c: false, d: null, };
    var bbb = { a: 1, b: 'b', c: false, d: null, e: undefined};
  }
  //(4)比较
  console.log('用,isDeepEqualA,比较',isDeepEqualA(aaa, bbb)); //false
  console.log('用,isDeepEqualB,比较',isDeepEqualB(aaa, bbb)); //false
4、深克隆
  //(1)方法1,value为undefined的键值对将消失
  function deepCloneA(arrayOrObject) {
    return JSON.parse(JSON.stringify(arrayOrObject));
  }  
  //(2)方法2
  function deepCloneB(arrayOrObject) {
    var target = null;
    //{ }.toString.call也可以用Object.prototype.toString.call代替
    if ({}.toString.call(arrayOrObject) === "[object Array]") target = [];
    if ({}.toString.call(arrayOrObject) === "[object Object]") target = {};
    for (var key in arrayOrObject) {
      var value = arrayOrObject[key];
      if ({}.toString.call(value) === "[object Array]" || {}.toString.call(value) === "[object Object]" ) {
        target[key] = deepCloneB(value);
      } else {
        target[key] = value;
      }
    }
    return target;
  }
  //(3)数据
  var aaa = { a: 1, b: 'b', c: false, d: null, e: undefined};
  var bbb = { a: 1, b: { c: 3 }, d: { e: [1, 2] } };
  //(4)克隆
  console.log('用,deepCloneA,克隆');
  console.log(deepCloneA(aaa)); 
  console.log(deepCloneA(bbb));
  console.log('用,deepCloneB,克隆');
  console.log(deepCloneB(aaa)); 
  console.log(deepCloneB(bbb));
 
二、字符串、数组、对象之为空、含某项、项位置、位置项、遍历、相似
1、字符串
  (1)为空的判断方法
    A、if(/^\s*$/.test(str))//只有0到多个空格时,为true
    B、if(!str.trim().length)//trim,修剪
  (2)有“某个字符”的判断方法,
    A、if(str.indexOf(item) != -1)
  (3)遍历的方法,for,for…in,for…of,
    var str = 'abc';
    for (var i = 0; i < str.length; i++) {//可以没有var
      console.log( 'for循环', str[i] );
    }
    for(var index in str){//可以没有var
      console.log( 'for-in循环', index,str[index] );
    }
    for(var item of str){//可以没有var
      console.log( 'for-of循环', item );
    }
  (4)获取item项的位置,str.indexOf(item)
  (5)获取index位置的项
    A、str.charAt(index)
    B、str[index]
2、数组
  (1)为空的判断方法
    A、if(JSON.stringify(ary)=="[]")//==字符串判断法,空数组里有空格,字符串里没有空格,结果也是true
    B、if(!ary.length)//==长度判断法,
  (2)有“某个项”的判断方法
    A、if(ary.indexOf(item) != -1)//首选方案
    B、if(ary.includes(item))
  (3)遍历的方法,for,for…in,for…of,另有forEach、map
    var array = ['a','b'];
    for (var i = 0; i < array.length; i++) {//可以没有var
      console.log( 'for循环', array[i] );
    }
    for(var index in array){//可以没有var
      console.log( 'for-in循环', index,array[index] );
    }
    for(var item of array){//可以没有var
      console.log( 'for-of循环', item );
    }
    // const person = {
    //   name: 'Alice',
    //   age: 30,
    // };
    // const entries = Object.entries(person); //返回一个数组,其中每个元素都是一个包含对象属性名和属性值的数组
    // console.log(entries);
    // for (const [key, value] of entries) {
    //   console.log( key, value );
    // }
  (4)获取item项的位置,ary.indexOf(item)
  (5)获取index位置的项,ary[index]
3、对象
  (1)空对象的判断方法
    A、if(JSON.stringify(obj)=="{}")//==字符串判断法,空对象里有空格,字符串里没有空格,结果也是true
    B、if(!Object.keys(obj).length)//==长度判断法,只包含可枚举属性
    C、if(!Object.getOwnPropertyNames(obj).length)//还包含不可枚举属性
  (2)对象里有“某个键值对”的判断方法
    A、if(key in obj)//首选方案
    B、if(obj.hasOwnProperty(key))//只会检查对象的自有属性
    C、注意,if(obj.key)不可以,因为排除了obj={key:0},obj={key:false},obj={key:null},obj={key:undefined}
  (3)遍历的方法,for…in
    var object = {
      a: 1,
      b: 2
    };
    for(var key in object){//可以没有var
      console.log( object[key] );
    }
  (4)获取key属性项的位置
    A、对象里面的项是无序的,如果有位置要求,则应该写成数组形式
  (5)获取index位置的项
    A、对象里面的项是无序的,如果有位置要求,则应该写成数组形式

三、字符串方法:所有方法均不改变原字符串
菜鸟教程,https://www.runoob.com/js/js-strings.html
MDN WEB,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String
注意,数组也有的方法,如concat、includes、indexOf、slice
1、charAt:通过索引获取字符,不存在时,返回空字符串。也可以用str[0]
2、charCodeAt:通过索引获取字符的unicode值
3、concat:把多个字符串拼接成一个字符串,相当于加号
4、includes 
  (1)作用:判断字符串中是否有另一字符串
  (2)参数:要判断的字符串
  (3)返回值:boolean
  (4)是否改变原字符串:不变
    var pets = 'catdogbat';
    console.log(pets.includes('cat'));//true
5、indexOf:通过字符获取(该字符首次出现时所在位置的)索引,不存在时,返回-1。
  console.log('20000' > '000');//true,前者是否包含后者,且不等于后者,又例location>"https"
  console.log('000' < '20000');//true,后者是否包含前者,且不等于前者 
6、localeCompare,在当前语言环境中,比较两个字符串的前后(先比较第1个字符,相同则比较第2个字符)
  (1)参数:字符串
  (2)返回值:-1,0,1,表示(前者在后者)之前、相等、之后
    console.log( 'a'.localeCompare('b') );
  (3)示例,字符串排序,借用数组的sort方法
    var str = 'SSSadsdcc1CCC荣的Adkdk辱kkkDc观c4dddaaa发货ccCCs6SSSSSs';
    str = str.split('').sort(function (one, two) {
      return one.localeCompare(two)//升序,反之为降序
    }).join('');
    console.log(str);
7、match,搜索一个字符串-能够-匹配某个模式-的字符,与正则中的exec相似
  (1)参数是正则,不加g,匹配一次;返回值为数组,[大正则第1次匹配到的内容,第1、2、3...个小分组匹配到的内容,索引,整个字符串,groups]
    var reg=/(\d)A(\d)/;
    var before="000002223A45556A7888";
    var after=before.match(reg);
    console.log('返回数组:',after);// [ '3A4', '3', '4', index: 8, input: '000002223A45556A7888', groups: undefined ]
  (2)参数是正则,加g,匹配多次;返回值为数组,[大正则第1次匹配到的内容,大正则第2次匹配到的内容,大正则第3次匹配到的内容,...]
    var reg=/(\d)A(\d)/g;
    var before="000002223A45556A7888";
    var after=before.match(reg);
    console.log('返回数组:',after);//[ '3A4', '6A7' ]
  (3)参数是字符串;返回值为数组,和不加g的正则一样
    const str = 'hello world';
    const result1 = str.match(/world/);
    const result2 = str.match('world');
    console.log(result1); //[ 'world', index: 6, input: 'hello world', groups: undefined ]
    console.log(result2); //[ 'world', index: 6, input: 'hello world', groups: undefined ]
    console.log(result2.index); //6
8、matchAll,搜索一个字符串-能够-匹配某个模式-的所有字符 
    var str = 'test-test2';
    var regexp = /test(\d?)/g;
    var array = [...str.matchAll(regexp)];
    console.log(array);
9、slice:复制字符串
  (1)没有参数时,返回整个字符串
  (2)一个参数时,返回参数到末尾的字符串
  (3)两个参数时(m,n),返回从索引m到索引n,不包括n
  另,console.log( '7654321'.slice(-3) );//从后往前取3项,构成的新字符串
10、split:把字符串按照指定的分隔符拆分成数组(与数组里join方法相反)
  console.log("ABCDEFG".split(""));//[ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ]
  console.log("ABCDEFG".split("D"));//[ 'ABC', 'EFG' ]
11、substr:复制字符串(弃用)
  (1)没有参数时,返回整个字符串
  (2)一个参数时,返回返回参数到末尾的字符串
  (3)两个参数时(m,n),返回从索引m开始的n个字符
12、substring:复制字符串
  (1)没有参数时,返回整个字符串
  (2)一个参数时,返回返回参数到末尾的字符串
  (3)两个参数时(m,n),返回索引m到索引n,不包括n
13、trim:去字符串前后空格
  var str="   hello world   "; 
  console.log('('+str+')'); 
  console.log('('+str.trim()+')'); 
14、replaceAll:原字符串不变,返回值为新字符串
    var before = "aba-aba-aba-aba-aba-aba-aba";
    var after = before.replaceAll('a',"b")
    console.log( before );//aba-aba-aba-aba-aba-aba-aba
    console.log( after );//bbb-bbb-bbb-bbb-bbb-bbb-bbb
15、replace:原字符串不变,返回值为新字符串
  (1)参数是“旧字符串+新字符串”时,旧字符串被新字符串取代
      var before='old--CCC--old';
      var after=before.replace("old","new");
      console.log('返回字串:',after);//new--CCC--old
  (2)参数是“正则+新字符串”时,
    A、不加g,匹配一次
      var reg=/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
      var before='AAA_12345678_BBB_87654321_CCC';
      var after=before.replace(reg,"0000000");
      console.log('返回字串:',after);//AAA_0000000_BBB_87654321_CCC
    B、加g,匹配多次
      var reg=/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/g;
      var before='AAA_12345678_BBB_87654321_CCC';
      var after=before.replace(reg,"0000000");
      console.log('返回字串:',after);//AAA_0000000_BBB_0000000_CCC
  (3)正则+回调函数作参数时,
    A、不加g,匹配一次
      var reg=/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
      var before='AAA_12345678_BBB_87654321_CCC';
      var i=0;
      var after=before.replace(reg,function(totalRegexp,A1,A2,A3,A4,A5,A6,A7,A8,firstIndex,totalString){
          i=i+1;
          console.log('"'+totalRegexp+'"是被大正则第1次匹配到的内容');
          console.log('"'+A1+'"是被第1个小正1则匹配上的内容');
          console.log('"'+A2+'"是被第2个小正1则匹配上的内容');
          console.log('"'+A3+'"是被第3个小正1则匹配上的内容');
          console.log('"'+A4+'"是被第4个小正1则匹配上的内容');
          console.log('"'+A5+'"是被第5个小正1则匹配上的内容');
          console.log('"'+A6+'"是被第6个小正1则匹配上的内容');
          console.log('"'+A7+'"是被第7个小正1则匹配上的内容');
          console.log('"'+A8+'"是被第8个小正1则匹配上的内容,以此类推');
          console.log('"'+firstIndex+'"是本次(大正1则匹配到的)字符串的第1个字符,在整个字符串的索引');
          console.log('"'+totalString+'"是整个字符串,本轮匹配结束');
          console.log("==========");
          return '(我将取代被大正1则第'+i+'次匹配到的内容)'
      });
      console.log('返回字串:',after);
      // 返回值为基于原字符串的新字符串,用函数第1次返回的内容-代替-原字符串第1次被大正则匹配到的内容;
      // "12345678"是被大正1则第1次匹配到的内容
      // "1"是被第1个小正1则匹配上的内容
      // "2"是被第2个小正1则匹配上的内容
      // "3"是被第3个小正1则匹配上的内容
      // "4"是被第4个小正1则匹配上的内容
      // "5"是被第5个小正1则匹配上的内容
      // "6"是被第6个小正1则匹配上的内容
      // "7"是被第7个小正1则匹配上的内容
      // "8"是被第8个小正1则匹配上的内容,以此类推
      // "4"是本次(大正1则匹配到的)字符串的第1个字符,在整个字符串的索引
      // "AAA_12345678_BBB_87654321_CCC"是整个字符串,本轮匹配结束
      // ==========
      // 返回字串: AAA_(我将取代被大正1则第1次匹配到的内容)_BBB_87654321_CCC
    B、加g,匹配多次
      var reg=/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/g;
      var before='AAA_12345678_BBB_87654321_CCC';
      var i=0;
      var after=before.replace(reg,function(totalRegexp,A1,A2,A3,A4,A5,A6,A7,A8,firstIndex,totalString){
          i=i+1;
          console.log('"'+totalRegexp+'"是被大正则第'+i+'次匹配到的内容');
          console.log('"'+A1+'"是被第1个小正1则匹配上的内容');
          console.log('"'+A2+'"是被第2个小正1则匹配上的内容');
          console.log('"'+A3+'"是被第3个小正1则匹配上的内容');
          console.log('"'+A4+'"是被第4个小正1则匹配上的内容');
          console.log('"'+A5+'"是被第5个小正1则匹配上的内容');
          console.log('"'+A6+'"是被第6个小正1则匹配上的内容');
          console.log('"'+A7+'"是被第7个小正1则匹配上的内容');
          console.log('"'+A8+'"是被第8个小正1则匹配上的内容,以此类推');
          console.log('"'+firstIndex+'"是本次(大正1则匹配到的)字符串的第1个字符,在整个字符串的索引');
          console.log('"'+totalString+'"是整个字符串,本轮匹配结束');
          console.log("==========");
          return '(我将取代第'+i+'次被大正1则匹配到的内容)'
      });
      console.log('返回字串:',after);
      // 返回值为基于原字符串的新字符串,用函数第1、2、3...次返回的内容-代替-原字符串第1、2、3...次被大正则匹配到的内容;
      // "12345678"是被大正1则第1次匹配到的内容
      // "1"是被第1个小正1则匹配上的内容
      // "2"是被第2个小正1则匹配上的内容
      // "3"是被第3个小正1则匹配上的内容
      // "4"是被第4个小正1则匹配上的内容
      // "5"是被第5个小正1则匹配上的内容
      // "6"是被第6个小正1则匹配上的内容
      // "7"是被第7个小正1则匹配上的内容
      // "8"是被第8个小正1则匹配上的内容,以此类推
      // "4"是本次(大正1则匹配到的)字符串的第1个字符,在整个字符串的索引
      // "AAA_12345678_BBB_87654321_CCC"是整个字符串,本轮匹配结束
      // ==========
      // "87654321"是被大正1则第2次匹配到的内容
      // "8"是被第1个小正1则匹配上的内容
      // "7"是被第2个小正1则匹配上的内容
      // "6"是被第3个小正1则匹配上的内容
      // "5"是被第4个小正1则匹配上的内容
      // "4"是被第5个小正1则匹配上的内容
      // "3"是被第6个小正1则匹配上的内容
      // "2"是被第7个小正1则匹配上的内容
      // "1"是被第8个小正1则匹配上的内容,以此类推
      // "17"是本次(大正1则匹配到的)字符串的第1个字符,在整个字符串的索引
      // "AAA_12345678_BBB_87654321_CCC"是整个字符串,本轮匹配结束
      // ==========
      // 返回字串: AAA_(我将取代第1次被大正1则匹配到的内容)_BBB_(我将取代第2次被大正1则匹配到的内容)_CCC
    C、罕见用法
      var reg=/(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/;
      var before='AAA_12345678_BBB_87654321_CCC';
      var after=before.replace(reg,'$1');
      //返回值为基于原字符串的新字符串,用第1个小分组的内容-代替-原字符串第1次被大正1则匹配到的内容;
      console.log('返回字串:',after); // AAA_1_BBB_87654321_CCC
16、字符串里字符重复次数
  (1)对象法:把一个字母作为对象的属性名,字母出现的次数作为属性值;进而比较属性值的大小。
    var str = 'SSSadsdccCCCAdkdkkkkDccdddaaaccCCsSSSSSs';
    function maxstr(str) {
      var obj = {};
      var maxNum = 0;
      var maxVal = "";
      for (var i = 0; i < str.length; i++) {
        var strNum = str.charAt(i).toLowerCase();
        if (obj[strNum]) {
          obj[strNum]++;
        } else {
          obj[strNum] = 1;
        }
      }
      for (var attr in obj) {
        if (obj[attr] > maxNum) {
          maxNum = obj[attr];
          maxVal = attr;
        } else if (obj[attr] == maxNum) {
          maxVal += attr
        }
      }
      return "出现次数最多的字母是:" + maxVal + ";次数是:" + maxNum
    }
    var valNum = maxstr(str);
    console.log(valNum);
  (2)排序法:先对它们进行排序,再取出小分组和小分组被匹配的次数。
    var str = 'SS张SadsdccCCC四Adkdkk张kkDccdddaa三acc四CCsS李SSSSs';
    function maxstr(str) {
      var maxNum = 0;
      var maxVal = "";
      str = str.split('').sort(function (one, two) {
        return one.localeCompare(two)
      }).join('');
      console.log("排序后的字符串是:" + str);
      //下面正则的意思是:"(\w)"字符、"\1"第1个括号、"+"1次到多次、"g全局"、"i"不区分大小写
      str.replace(/(\w)\1+/gi, function (bigReg,item) {
        if (bigReg.length > maxNum) {
          maxNum = bigReg.length;
          maxVal = item;
        } else if (bigReg.length == maxNum) {
          maxVal += item;
        }
      });
      return "出现次数最多的字母是:" + maxVal + ",次数" + maxNum;
    }
    var valNum = maxstr(str);
    console.log(valNum);

四、正则
  附、修饰符
    (1)g,使用全局匹配
    (2)i,忽略大小写
  附、正则实例获取
    (1)console.log(/ab+c/i);// /ab+c/i
    (2)console.log(new RegExp("ab+c", "i"));// /ab+c/i
    (3)console.log(new RegExp(/ab+c/, "i"));// /ab+c/i
1、test,检测一个字符串-是否-匹配某个模式
  var before='程序员:123';
  var reg=/\d/;
  var after=reg.test(before);
  console.log('返回布尔值:',after);
2、exec,搜索一个字符串-能够-匹配某个模式-的字符,与字符串中的match相似
  (1)不加g,匹配一次;返回值为数组,[大正则第1次匹配到的内容,第1、2、3...个小分组匹配到的内容,索引,整个字符串,groups]
    var reg=/(\d)A(\d)/;
    var before="000002223A45556A7888";
    var after=reg.exec(before);
    console.log('返回数组:',after);
    console.log('索引:',after.index);
  (2)加g,匹配多次;返回值为数组,[大正则第1次匹配到的内容,大正则第2次匹配到的内容,大正则第3次匹配到的内容,...]
    var reg=/(\d)A(\d)/g;
    var before="000002223A45556A7888";
    var after=reg.exec(before);
    console.log('返回数组:',after);
3、正则元字符
  (1)\w:匹配数字、字母、下划线
  (2)\W:匹配非数字、字母、下划线
  (3)\n:匹配换行符
  (4).:匹配非换行符
  (5)\s:匹配空格
  (6)\S:匹配非空格
  (7)^:匹配输入字符串的开始位置;在方括号中使用时,表示排除方括号中的所有字符
  (8)$:匹配输入字符串的结尾位置
  (9)*:匹配前面的子表达式0-n次 
  (10)+:匹配前面的子表达式1-n次
  (11)?:匹配前面的子表达式0或1次
  (12)\r:匹配回车  
  (13)正则元字符的使用:MAC 
    var MACMatch=/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){5}$|^[A-Fa-f0-9]{2}(-[A-Fa-f0-9]{2}){5}$/,
4、用正则封装解析方法(解析URL中的问号参数值以及HASH值) 
  function queryUrlParam1(url) {
    var obj = {};
    var reg1 = /([^?=&#]+)=([^?=&#]+)/g;
    var reg2 = /#([^?=&#]+)/;
    url.replace(reg1, function (keyValue,key,value) {
      obj[key] = value;
    });
    if (reg2.test(url)) {
      obj['HASH'] = reg2.exec(url)[1];
    }
    return obj;
  }
  function queryUrlParam2(url) {
    var  obj = {};
    var askText = '';
    var hashText = '';
    var askIndex = url.indexOf('?') === -1 ? url.length : url.indexOf('?');
    var hashIndex = url.indexOf('#') === -1 ? url.length : url.indexOf('#');
    if (askIndex < hashIndex) {
        askText = url.substring(askIndex + 1, hashIndex);
        hashText = url.substring(hashIndex + 1);
    } else {
        askText = url.substring(askIndex + 1);
        hashText = url.substring(hashIndex + 1, askIndex);
    }
    if (askText) {
        askText.split('&').forEach(function(item){
          let [key, value] = item.split('=');
          obj[key] = value;
        });
    }
    if (hashText) obj['HASH'] = hashText;
    return obj;
  };
  var str = "https://www.baidu.com/newspage/data/landingsuper?AAA=1111&BBB=222&CCC=333#1234"
  console.log(queryUrlParam1(str));
  console.log(queryUrlParam2(str));
  var baidu = queryUrlParam1("https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=%E5%9C%B0%E5%9D%80%E6%A0%8F%20js%E4%BA%92%E8%BD%AC&rn=50&oq=%25E5%259C%25B0%25E5%259D%2580%25E6%25A0%258F%25E7%25BC%2596%25E7%25A0%2581%25E4%25B8%258E%25E6%25B1%2589%25E5%25AD%2597%2520js%25E4%25BA%2592%25E8%25BD%25AC&rsv_pq=d283c8db0000cb86&rsv_t=c351Ro%2FA%2FmhiIiW6LtAjCUz3gfW%2FzY%2FIdrQZsY%2BfrFrUBGGwf1OSd4mVEUY&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_btype=t&inputT=2982&rsv_sug3=7&rsv_sug1=7&rsv_sug7=100&bs=%E5%9C%B0%E5%9D%80%E6%A0%8F%E7%BC%96%E7%A0%81%E4%B8%8E%E6%B1%89%E5%AD%97%20js%E4%BA%92%E8%BD%AC")
  var wd = decodeURIComponent(baidu.wd) 
  var oq = decodeURIComponent(decodeURI(baidu.oq))
  var bs = decodeURIComponent(baidu.bs) 
  var rsv_t = decodeURIComponent(baidu.rsv_t) 
  var rsv_pq = baidu.rsv_pq 
  console.log('baidu...',baidu);
  console.log('wd...',wd);
  console.log('oq...',oq);
  console.log('bs...',bs);
  console.log('rsv_t...',rsv_t);
  console.log('rsv_pq...',rsv_pq);
5、用正则封装解析方法(HASH值) 
  function hashA(url){
    var reg=/#([^#&=?]+)/;
    if(reg.test(url)){
      return reg.exec(url)[1];
    }
  }
  console.log(hashA("https://www.baidu.com/newspage/data/landingsuper?AAA=1111&BBB=222&CCC=333#1234"));
6、用正则封装解析方法
  var ary = ["零","一","二","三","四","五","六","七","八","九"];
  var str = "今年是2017年";
  var result = str.replace(/\d/g,function (value) {
    return ary[value]
  });
  console.log( result );
7、用正则择出汉字
  var str = '<img src="haha.png" alt="你的朋友"/>';
  var reg=/alt="(.+)"/;
  console.log(reg.exec(str)[1]);
8、someNumber为数字,当它的小数位数少于number时,则保留原数字,否则四舍五入保留至小数number位
  function formatTime(someNumber, number) { 
    var lastNum = null;
    if (typeof someNumber == "number") {
      var strNum = someNumber.toString().match(/\.\d*/);
      if (strNum == null || strNum[0].length <= number + 1) {
        lastNum = someNumber;
      } else {
        lastNum = someNumber.toFixed(number);
      }
    }
    return lastNum;
  }
  console.log(formatTime(3.1283456, 2));
  console.log(formatTime(3.55, 3));
  console.log(formatTime(3, 3));

五、数组方法
菜鸟教程,https://www.runoob.com/js/js-obj-array.html
MDN WEB,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
注意,数组也有的方法,如concat、includes、indexOf、slice
附、区别
  (1)every、some,返回true或false
  (2)filter、find,返回多项或单项
1、concat
  (1)作用:把多个数组拼接在一起
  (2)参数:要拼接的数组或字符串。
    如果是数组,就把数组里的每一项放到原数组的后面;
    如果是字符串,把字符串作为一项,放在原数组后面;
    浅克隆数组:ary.concat();ary.concat([]);
    var obj = {b:3}
    var ary = [1,2,obj]
    var aaa = ary.concat();
    console.log( obj == aaa[2] ); //true
  (3)返回值:拼接好的数组
  (4)是否改变原数组:不变
2、every
  (1)方法用于检测数组的所有元素是否-都-满足指定条件,返回值为boolean
    const array1 = [1, 30, 39, 29, 10, 13];
    console.log(array1.every((curr) => curr < 40));//true
3、filter
  (1)作用:找出所有满足条件的项
  (2)参数:匿名函数,匿名函数可以有三个参数
  (3)返回值:所有满足条件的项组成的数组
    const array = [10, 20, 30, 40, 50];
    const single = array.filter(function(num){
      return num > 30
    });
    console.log(single, typeof single); //[ 40, 50 ] object
  (4)是否改变原数组:不变
  (5)示例一
    var aryOld = [1, 2, 3, 4, 5];
    var aryNew=aryOld.filter(function(item){
        return item > 3;
    });
    console.log(aryNew);
  (6)示例二
    var todos = [{
        score: 10,
        completed: true
    }, {
        score: 2,
        completed: false
    }, {
        score: 30,
        completed: true
    }, {
        score: 40,
        completed: true
    }, {
        score: 5,
        compelted: false
    }];
    var trueScore = todos.filter(function(item){
        return item.completed;
    });
    console.log(trueScore);
4、find
  (1)作用:找出最先满足条件的那一项
  (2)参数:匿名函数,匿名函数可以有三个参数
  (3)返回值:最先满足条件的那一项
    const array = [10, 20, 30, 40, 50];
    const single = array.find(function(num){
      return num > 30
    });
    console.log(single, typeof single); //40 number
  (4)是否改变原数组:不变
  (5)示例一
    var ary=[1, 5, 10, 15]
    ary.find(function(value) {  
      return value > 9;  
    }) // 10  
  (6)示例二
    var studentAll = [
    {
        name: '张三',
        gender: '男',
        age: 20
    },
    {
        name: '王小毛',
        gender: '男',
        age: 20
    },
    {
        name: '李四',
        gender: '男',
        age: 20
    }
    ];
    var studentOne=studentAll.find(function (ele) {
        return ele.age == 20
    });
    console.log(studentOne);
5、forEach
  (1)作用:循环遍历每一项,循环次数为数组项数
  (2)参数:匿名函数,匿名函数可以有三个参数,项、项索引、数组
  (3)返回值:undefined
  (4)是否改变原数组:不变
6、includes 
  (1)作用:判断数组中是否有某项
  (2)参数:要判断的数组项
  (3)返回值:boolean
  (4)是否改变原数组:不变
    var pets = ['cat', 'dog', 'bat'];
    console.log(pets.includes('cat'));//true
7、indexOf
  (1)作用:获取数组项第一次在数组中出现的位置索引
  (2)参数:要判断的数组项
  (3)返回值:数组项的索引,没有该项返回-1
  (4)是否改变原数组:不变
8、join(与字符串里split方法相反)
  (1)作用:把数组的每一项按照指定的分隔符拼接成字符串
  (2)参数:指定的分隔符,项与项之间用分隔符连接
  (3)返回值:拼接好的字符串
  (4)是否改变原数组:不变
  (5)实例1:console.log(["ABC", "EFG"].join(""));//ABCEFG
  (6)实例2:console.log(["ABC", "EFG"].join("D"));//ABCDEFG
9、map
  (1)作用:循环遍历每一项,循环次数为数组项数
  (2)参数:匿名函数,匿名函数可以有三个参数,项、项索引、数组
  (3)返回值:返回一个新数组,新数组的每一项是匿名函数每次执行后的返回值
  (4)是否改变原数组:不变
10、pop
  (1)作用:删除项数组末尾项
  (2)参数:没有参数
  (3)返回值:被删除的项
  (4)是否改变原数组:改变
11、push
  (1)作用:向数组末尾追加一项
  (2)参数:要添加的数组项(多项用逗号隔开)
  (3)返回值:新数组的长度
  (4)是否改变原数组:改变
12、reduce
  (1)累加,将最后一次计算结果暴露出去;可以作为高阶函数,用于函数的compose
  (2)接受四个参数:初始值(或回调函数上1次的返回值),当前元素值,当前索引,调用 reduce 的数组
  (3)示例一
    var ary=[1,3,2,4,3,2];
    var aryTotal=ary.reduce(function (total,num) {
        return total+num;
    });
    console.log(aryTotal);
  (4)示例二
    var todos = [{
        score: 12,
        completed: true
    }, {
        score: 21,
        completed: false
    }, {
        score: 35,
        completed: true
    }, {
        score: 47,
        completed: true
    }, {
        score: 95,
        completed: false
    }];
    const totalScore = todos.reduce(function(total, item){
        return item.completed ? total + item.score : total
    },10000);
    console.log(totalScore);
13、reverse
  (1)作用:倒序数组
  (2)参数:不需要参数
  (3)返回值:倒序后的数组
  (4)是否改变原数组:改变
     示例:console.log(['21', 'a', 'e', 'c'].reverse());
14、shift
  (1)作用:删除数组开头项
  (2)参数:没有参数
  (3)返回值:被删除的项
  (4)是否改变原数组:改变
15、slice
  (1)作用:复制数组项
  (2)没有参数:原数组不变,返回整个数组
  (3)一个参数:返回从索引位置到末尾
  (4)两个参数(n,m):返回索引n到m,不包含m
  (5)是否改变原数组:不变
  另,console.log( [7,6,5,4,3,2,1].slice(-3) );//从后往前取3项,构成的新数组
16、some
  (1)方法用于检测数组的所有元素是否-有-满足指定条件,返回值为boolean
    const array1 = [1, 30, 39, 29, 10, 13];
    console.log(array1.some((curr) => curr < 10));//true
17、sort
  (1)作用:按照指定规则排序
  (2)参数:
    A、无
      var ary = ['21', 'a', 32, '4', 5, '61', 'e', 'f', 'd', 'b', 'c'];
      ary.sort(); //升序,先比较每项的第1个字符,相同则比较第2个字符
      console.log(ary)
    B、回调函数-纯数字项数组
      var ary = [21, 32, 32, 5, 61, 61, 21, 4, 5, 61];
      ary.sort(function (a, b) { 
        return a - b //升序,比较数字的大小
      });
      console.log(ary)
    C、回调函数-任意项数组
      var ary = [
        { id: 2, name: 'b', age: 18 },
        { id: 3, name: 'c', age: 20 },
        { id: 1, name: 'a', age: 19 }
      ];
      ary.sort(function (a,b) {
        //return a.id - b.id
        return a.name.localeCompare(b.name) ; //返回值为-1,0,1,表示(前者在后者)之前、相等、之后
      });
      console.log(ary)
  (3)返回值:排好序的数组
  (4)是否改变原数组:改变
18、splice
  (1)作用:参数不同,作用不同,主要用来删除数组项
  (2)没有参数:原数组不变,返回空数组
  (3)一个参数:从索引位置删除到末尾,返回被删除项构成的数组
  (4)两个参数(n,m):从索引n删除m个,返回被删除项构成的数组
  (5)三个参数(n,m,X): 从索引n删除m个,被删除项用X填补,返回被删除项构成的数组
  (6)是否改变原数组:改变
  /* ===以上原数组改变;以下原数组不变=== */
19、unshift
  (1)作用:向数组开头添加一项
  (2)参数:要添加的数组项(多项用逗号隔开)
  (3)返回值:新数组的长度
  (4)是否改变原数组:改变 
20、call和apply
  (1)call的第2、3、4个参数,分别对应函数的第1、2、3个参数
  (2)apply的第2个参数是一个数组,数组的第1、2、3项,分别对应函数的第1、2、3个参数
    示例一
      function test(a,b) {
        console.log(this)  //默认情况下函数中的this指向的是Window对象
        console.log(a,b)  //a,b对应的是参数1和参数2
      }
      const obj= {
        name:'zhangsan'
      }
      test.call(obj,'参数1','参数2')
      test.apply(obj,['参数1','参数2']) //数组的第一、二项,分别对应参数的第一、二个
    示例二
      var array1 = [1,1,1,1,1];
      var array2 = [2,2,2,2,2];
      array1.push.call(array2,6,7,8,9);//改变了array2
      array2.push.apply(array1,[9,8,7,6]);//改变了array1
      console.log(array1)
      console.log(array2)
21、数组4种去重方法
  (1)新数组法:重新构建一个新数组,遍历原数组的每一项,如果该项在新数组的索引为-1,则把该项添加到新数组中。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary){
      var newAry=[];
      for(var i=0; i<ary.length; i++){
        if(newAry.indexOf(ary[i])===-1){
            newAry[newAry.length]=ary[i];
        }
      }
      return newAry
    }
    console.log(removeMany(ary))
  (2)新对象法:如果对象中没有这一项,给对象添加属性名和属性值,如果对象中已经有了,说明是重复的,直接在数组中删除这一项;
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary){
      var obj={};
      for(var i=0; i<ary.length; i++){
        var cur=ary[i];
        if(obj[cur]===cur){//说明重复了
            ary.splice(i,1);
            i--;
        }else{
            obj[cur]=cur;
        }
      }
      return ary
    }
    console.log(removeMany(ary))
  (3)sort排序法:先用sort方法对数组进行排序,然后拿前一项与后一项进行比较,如果相等,则删除前一项,同时为了防止数组塌陷,下一次仍从该项开始比较。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary){
      ary.sort(function (a, b) {
        return a - b;//升序
      });
      for (var i = 0; i < ary.length; i++) {
        if (ary[i] === ary[i + 1]) {
          ary.splice(i, 1);
          i--;
        }
      }
      return ary
    }
    console.log(removeMany(ary))
  (4)双循环法:第一个循环,依次取出数组的每一项A;第二个循环,从数组的A项开始,依次与A项进行比较,如果相等则删除,同时为了防止数组塌陷,下一次仍从该项开始比较。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary){
      for (var i = 0; i < ary.length; i++) {
        var cur = ary[i];
        for (var j = i + 1; j < ary.length; j++) {
          if (cur === ary[j]) {
            ary.splice(j, 1);
            j--;
          }
        }
      }
      return ary
    }
    console.log(removeMany(ary))
22、数组3种排序方法
  (1)冒泡排序(目标:实现升序排列)
    var ary = [5, 4, 3, 2, 1];
    function bubbleSort(ary) {
      for (var i = 0; i < ary.length - 1; i++) {
        //正在进行第几次循环
        for (var j = 0; j < ary.length - i - 1; j++) {
          //本次循环需要执行几次
          if (ary[j] > ary[j + 1]) {
            var tmp = ary[j];
            ary[j] = ary[j + 1];
            ary[j + 1] = tmp;
          }
        }
        console.log(ary);
      }
      return ary;
    }
    console.log(bubbleSort(ary))
  (2)插入排序(目标:实现升序排列)
    var ary = [50, 40, 30, 20, 10];
    function insertSort(ary) {
      var left = ary.splice(0, 1);
      for (var i = 0; i < ary.length; i++) {
        //正在进行第几次循环
        var aryAry = ary[i];
        for (var j = left.length - 1; j >= 0; ) {
          //本次循环需要执行几次
          var leftAry = left[j];
          if (aryAry < leftAry) {
            j--;
            if (j === -1) {
              left.unshift(aryAry);
            }
          } else {
            left.splice(j + 1, 0, aryAry);
            break;
          }
        }
      }
      return left;
    }
    console.log(insertSort(ary));
  (3)快速排序(目标:实现升序排列)
    var ary = [500, 400, 300, 200, 100];
    function quickSort(ary) {
      if (ary.length <= 1) {
        return ary;
      }
      var num = Math.floor(ary.length / 2);
      var numValue = ary.splice(num, 1)[0];
      var left = [];
      var right = [];
      for (var i = 0; i < ary.length; i++) {
        var cur = ary[i];
        if (cur < numValue) {
          left.push(cur);
        } else {
          right.push(cur);
        }
      }
      return quickSort(left).concat([numValue], quickSort(right));
    }
    console.log(quickSort(ary));

六、对象方法
菜鸟教程,https://www.runoob.com/jsref/dom-obj-object.html
MDN WEB,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
1、Object.assign(target,...sources),用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象
  const target = { a: 1, b: 2 };
  const source = { b: 3, c: 4 };
  const result = Object.assign(target, source);
  console.log(result); // { a: 1, b: 3, c: 4 }
2、Object.create(proto, [propertiesObject]),创建一个新对象,使用指定的原型对象和可选的属性描述符对象
  const personProto = {
    sayHello() {
      console.log('Hello!');
    },
  };
  const person = Object.create(personProto);
  person.sayHello(); // Hello!
3、Object.defineProperty(obj, prop, descriptor),用于定义对象的新属性或修改现有属性,并返回修改后的对象
  const obj = {};
  Object.defineProperty(obj, 'property1', {
    value: 42,
    writable: false,
  });
  console.log(obj.property1); // 42
  obj.property1 = 43; // 由于 writable 为 false,赋值操作无效
  console.log(obj.property1); // 42
4、Object.entries(obj),返回一个给定对象自身可枚举属性的键值对数组
  const obj = { a: 1, b: 2 };
  const entries = Object.entries(obj);
  console.log(entries); // [ [ 'a', 1 ], [ 'b', 2 ] ]
5、Object.freeze(obj),冻结一个对象,使其不可修改、添加或删除属性
  const obj = { a: 1 };
  Object.freeze(obj);
  obj.a = 2; // 操作被忽略,因为对象已被冻结
  console.log(obj.a); // 1
6、Object.is(obj1, obj2),确定两个值是否是相同的值,与严格相等运算符(===)类似
  console.log(Object.is(2, 2)); // true
  console.log(Object.is(NaN, NaN)); // true
  console.log(Object.is(0, -0)); // false
  console.log(Object.is({a:1}, {a:1})); // false
7、Object.keys(obj),返回一个给定对象自身可枚举属性的名称数组
  const obj = { a: 1, b: 2 };
  const keys = Object.keys(obj);
  console.log(keys); // [ 'a', 'b' ]
8、Object.values(obj),返回一个给定对象自身可枚举属性值的数组
  const obj = { a: 1, b: 2 };
  const values = Object.values(obj);
  console.log(values); // [ 1, 2 ]

七、递归
  注意,递归前从外向里执行。递归后从里向外执行。
1、虚拟DOM中的递归
  //虚拟DOM,Virtual DOM,堆内存的一个JS对象,包含很多虚拟节点VNode,Virtual Node
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>创建一个虚拟DOM实例</title>
      <style>
        .list {
          color: green;
          font-size: 30px;
        }
      </style>
    </head>
    <body>
      <div>
        <div>创建一个虚拟DOM实例</div>
      </div>
      <script>
        function Element(obj) {
          this.tagName = obj.tagName;
          this.props = obj.props || {};
          this.children = obj.children || [];
        }
        Element.prototype.render = function () {
          var el = document.createElement(this.tagName);
          var props = this.props;
          for (propName in props) {
            propValue = props[propName];
            el.setAttribute(propName, propValue);
          }
          this.children.forEach(function (child) {
            var elemChild = null;
            if (child instanceof Element) {
              elemChild = child.render();//从外向里逐个折回。看最后一次递归执行时,发生了什么。
            } else {
              elemChild = document.createTextNode(child);//最后一次递归执行时,要经过这里。
            }
            el.appendChild(elemChild);//从里向外逐个添加。
          });
          return el;
        };
        var elem = new Element({
          tagName: 'ul',
          props: { 'class': 'list' },
          children: [
            new Element({
              tagName: 'li',
              children: [
                new Element({ tagName: 'span', children: ['我是span1','我是span1','我是span1--'] }),
                new Element({ tagName: 'span', children: ['我是span2','我是span2','我是span2'] })
              ]
            }),
            new Element({ tagName: 'li', children: ['我是span3','我是span3','我是span3',] })
          ]
        });
        var elemSelf = elem.render();
        document.querySelector('body').appendChild(elemSelf);
      </script>
    </body>
  </html>
2、快速排序中的递归
  var ary = [500, 400, 300, 200, 100];
  function quickSort(ary) {
    if (ary.length <= 1) {
      return ary;
    }
    var num = Math.floor(ary.length / 2);
    var numValue = ary.splice(num, 1)[0];
    var left = [];
    var right = [];
    for (var i = 0; i < ary.length; i++) {
      var cur = ary[i];
      if (cur < numValue) {
        left.push(cur);
      } else {
        right.push(cur);
      }
    }
    return quickSort(left).concat([numValue], quickSort(right));
  }
  console.log(quickSort(ary));
3、react框架中的递归
	(function (allFn) {
		if (typeof exports === "object" && typeof module !== "undefined") {
			module.exports = allFn()
		} else if (typeof define === "function" && define.amd) {
			define([], allFn)
		} else {
			var tempGlobal;
			if (typeof window !== "undefined") {
				tempGlobal = window
			} else if (typeof global !== "undefined") {
				tempGlobal = global
			} else if (typeof self !== "undefined") {
				tempGlobal = self
			} else {
				tempGlobal = this
			}
			tempGlobal.React = allFn()
		}
	})(function () {
		var define, module, exports;
		return (function outerFn(first, second, third) {
			function recursion(number) {
				if (!second[number]) {
					if (!first[number]) {
						var error = new Error("Cannot find module '" + number + "'");
						throw error.code = "MODULE_NOT_FOUND", error
					}
					var module = second[number] = {
						exports: {}
					};
					first[number][0].call(module.exports, function (key) {
						var value = first[number][1][key];
						return recursion(value ? value : key)
					}, module, module.exports, outerFn, first, second, third)
				}
				return second[number].exports//在react实例化的过程中,这行代码不但因获取依赖而多次执行,而且还因获取react实例而最后执行。
			}
			for (var number = 0; number < third.length; number++) recursion(third[number]);//fn(16)第1次执行,执行结果没有变量接收
			return recursion //执行到这,整个逻辑就快结束了。前两行可以合并为一行:return recursion(third[0]),同时下面的"(48)"应当删掉。 
		})(
			{ 2: [function (_dereq_, module, exports) { var thisVar = _dereq_(138) }, { "25": 25, "30": 30 }], },
			{ }, 
			[16]
		)(16)// fn(16)第2次执行,因为n[num]为真,所以直接返回n[num].exports并被挂在g.React上 
	});

八、两行布局
1、固定高,此增彼减;用flex计算下高
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        .full {
          height: 600px;
          background: red;
          display: flex;/* 相关代码 */
          flex-direction: column;/* 相关代码 */
        }
        .up {
          background: blue;
        }
        .down {
          background: green;
          flex: 1;/* 相关代码 */ /*height:100%;*/ 
        }
      </style>
    </head>
    <body>
      <div class="full">
        <div class="up">
          <p>ABCD</p>
        </div>
        <div class="down">
          <p>DCBA</p>
        </div>
      </div>
    </body>
  </html>
1、正好占满全屏
  (1)用calc计算下高、下右宽
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
          * {
            padding: 0;
            margin: 0;
          }
          html, body {
            height: 100%;
          }
          .up {
            height: 50px;
            background: #BBE8F2;
            width: 100%;
          }
          .down {
            height: calc(100% - 60px);/* 相关代码 */
            background: #D9C666;
            margin-top: 10px;
            font-size: 0;
            display: flex;/* 相关代码 */
          }
          .left {
            width: 200px;
            height: 100%;
            overflow: auto;
            background: palevioletred;
            margin-right: 10px;
            font-size: 16px;
          }
          .right {
            width: calc(100% - 210px);/* 相关代码 */
            height: 100%;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
          }
        </style>
      </head>
      <body>
        <div class="up"></div>
        <div class="down">
          <div class="left">
          </div>
          <div class="right">
          </div>
        </div>
      </body>
    </html>
  (2)用flex计算下高、下右宽
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
          * {
            padding: 0;
            margin: 0;
          }
          html, body {
            height: 100%;
          }
          .body {
            display: flex;
            flex-direction: column;
          }
          .up {
            height: 50px;
            background: #BBE8F2;
            width: 100%;
          }
          .down {
            flex: 1;
            background: #D9C666;
            font-size: 0;
            display: flex;
          }
          .left {
            width: 200px;
            height: 100%;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
            margin-right: 10px;
          }
          .right {
            flex: 1;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
          }
        </style>
      </head>
      <body class="body">
        <div class="up"></div>
        <div class="down">
          <div class="left"></div>
          <div class="right"></div>
        </div>
      </body>
    </html>
    
九、两列布局 
  附、两个需求 
    (1)左侧固定宽、右侧占满余屏 
    (2)左、右自适应高 
  附、四个实现 
    (1)table 
    (2)flex 
    (3)内外补+浮动 
    (4)大高赋给小高 
1、table 
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <title>两列高度自适应</title>
      <style type="text/css">
        * { margin: 0; padding: 0; }
      </style>
    </head>
    <body>
      <table style="width:100%">
        <tr>
          <td style="width:200px;background: #ddd;">
            <br/>
          </td>
          <td style="width:calc(100% - 200px);background: #ddd;">
            <br/>
            <br/>
            <br/>
            <br/>
          </td>
        </tr>
      </table>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html> 
2、flex
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        * { margin: 0; padding: 0; }
      </style>
    </head>
    <body>
      <div style="display:flex">
        <div style="width:200px;background: #ddd;margin-right: 10px;"><br/></div>
        <div style="flex:1;background: #ddd;"><br/><br/><br/><br/><br/></div>
      </div>
    </body>
  </html>   
3、内外补+浮动
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>两列高度自适应</title>
      <style type="text/css">
        * {
          margin: 0;
          padding: 0;
        }
        #main {
          overflow: hidden;
        }
        .both {
          background: #ddd;
          padding-bottom: 10000px;
          margin-bottom: -10000px;
        }
        .left {
          float: left;
          width: 200px;
        }
        .right {
          margin-left: 210px;
        }
      </style>
    </head>
    <body>
      <div id="main">
        <div class="left both"><br/></div>
        <div class="right both"><br/><br/><br/><br/><br/></div>
      </div>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
4、大高赋给小高
  (1)浮动
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
          * {
            margin: 0;
            padding: 0;
          }
          #left {
            float: left;
            width: 200px;
            background: #ddd;
          }
          #right {
            margin-left: 210px;
            background: #ddd;
          }
        </style>
      </head>
      <body>
        <div>
          <div id="left"><br/><br/><br/></div>
          <div id="right"><br/><br/><br/><br/><br/><br/><br/></div>
        </div>
        <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var right = document.getElementById("right");
      var leftHeight = parseFloat(getComputedStyle(left).height);
      var rightHeight = parseFloat(getComputedStyle(right).height);
      var distanceHeight = leftHeight - rightHeight;
      if (distanceHeight > 0) {
        right.style.height = leftHeight + "px";//right.style.height =rightHeight +distanceHeight+ "px";
        //right.style.height只能赋值,不能获取,所以此处用rightHeight代替right.style.height      
      } else {
        left.style.height = rightHeight + "px";//left.style.height = leftHeight-distanceHeight + "px";
      }
    </script>
  (2)定位
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>左右固定宽,中间自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .left {
            top: 0;
            left: 0;
            position: absolute;
            width: 200px;
            background: #ddd;
          }
          .right {
            top: 0;
            left: 210px;
            right: 0;
            position: absolute;
            background: #ddd;
          }
        </style>
      </head>
      <body>
        <div id="left" class="left"><br/><br/><br/><br/><br/><br/></div>
        <div id="right" class="right"><br/></div>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var right = document.getElementById("right");
      var leftHeight = parseFloat(getComputedStyle(left).height);
      var rightHeight = parseFloat(getComputedStyle(right).height);
      var distanceHeight = leftHeight - rightHeight;
      if (distanceHeight > 0) {
        right.style.height = leftHeight + "px";//right.style.height =rightHeight +distanceHeight+ "px";
        //right.style.height只能赋值,不能获取,所以此处用rightHeight代替right.style.height      
      } else {
        left.style.height = rightHeight + "px";//left.style.height = leftHeight-distanceHeight + "px";
      }
    </script>
5、jQuery实现多div等高(与方案4相似)
  $(function() {
    var maxHeight = 0;
    //以下求最高的div的高度
    $(".height").each(function() {
      var thisHeight = $(this).innerHeight();
      maxHeight = thisHeight > maxHeight ? thisHeight : maxHeight;
    })
    //以下将最高的div的高度赋值给每一个(需要与最高div等高的)div,
    $(".height").each(function() {
      var thisPadding = $(this).innerHeight() - $(this).height();
      //在jQuery中,innerheight=padding+内容height
      $(this).height(maxHeight - thisPadding);
    })
  })

十、三列布局
  附、两个需求
    (1)左右两侧固定宽、中间占满余屏
    (2)左、中、右自适应高
  附、四个实现
    (1)table
    (2)flex
    (3)内外补+浮动
    (4)大高赋给小高
1、table
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <title>两列高度自适应</title>
      <style type="text/css">
        * {
          margin: 0;
          padding: 0;
        }
      </style>
    </head>
    <body>
    <table style="width:100%">   
      <tr>
        <td style="width:200px;background: #ddd;"><br/></td>
        <td style="width:calc(100% - 400px);background: #ddd;"><br/><br/><br/><br/></td>
        <td style="width:200px;background: #ddd;"><br/></td>
      </tr>
    </table>
    <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
2、flex
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>左右固定宽,中间自适应</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        .flex {
          display: flex;
        }
        .leftAndRight{
          width: 200px;
          background: #ddd;
        }
        .middle{
          flex: 1;
          margin:0 10px;
          background: #ddd;
        }
      </style>
    </head>
    <body>
      <div class="flex">
        <div class="leftAndRight"><br/></div>
        <div class="middle"><br/><br/><br/><br/></div>
        <div class="leftAndRight"><br/></div>
      </div>
    </body>
  </html>
3、内外补+浮动
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>三列高度自适应</title>
      <style type="text/css">
        * {
          margin: 0;
          padding: 0;
        }
        #main {
          overflow: hidden;
        }
        .three{
          background: #ddd;
          padding-bottom: 10000px;
          margin-bottom: -10000px;
          float: left; 
        }
        .leftAndRight {
          width: 200px;
        }
        .middle {
          margin-left: 10px;
          margin-right: 10px;
          width:calc(100% - 420px)
        }
      </style>
    </head>
    <body>
      <div id="main">
        <div class="leftAndRight three"><br/></div>
        <div class="middle three"><br/><br/><br/><br/><br/></div>
        <div class="leftAndRight three"><br/></div>
      </div>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
4、大高赋给小高
  (1)浮动
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>三列高度自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .three{
            background: #ddd;
            float: left; 
          }
          .leftAndRight {
            width: 200px;
          }
          .middle {
            margin-left: 10px;
            margin-right: 10px;
            width:calc(100% - 420px)
          }
        </style>
      </head>
      <body>
        <div>
          <div class="leftAndRight three" id="left"><br/></div>
          <div class="middle three" id="middle"><br/><br/><br/><br/><br/></div>
          <div class="leftAndRight three" id="right"><br/></div>
        </div>
        <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var middle = document.getElementById("middle");
      var right = document.getElementById("right");
      var leftHeight=parseFloat(getComputedStyle(left).height);
      var middleHeight=parseFloat(getComputedStyle(middle).height);
      var rightHeight=parseFloat(getComputedStyle(right).height);
      var maxHeight=0;
      if(leftHeight-middleHeight>0){
          maxHeight= leftHeight;
      }else{
          maxHeight= middleHeight;
      }
      if(rightHeight-maxHeight>0){
          maxHeight= rightHeight;
      }
      left.style.height =maxHeight+ "px";
      middle.style.height =maxHeight+ "px";
      right.style.height =maxHeight+ "px"; 
    </script>
  (2)定位
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>左右固定宽,中间自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .leftAndRight {
            top: 0;
            width: 200px;
            position: absolute;
            background: #ddd;
          }
          .left {
            left: 0;
          }
          .right {
            right: 0;
          }
          .middle {
            top: 0;
            left: 210px;
            right: 210px;
            background: #ddd;
            position: absolute;
          }
        </style>
      </head>
      <body>
        <div id="left" class="left leftAndRight"><br/><br/></div>
        <div id="middle" class="middle"><br/><br/><br/><br/><br/><br/></div>
        <div id="right" class="right leftAndRight"><br/></div>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var middle = document.getElementById("middle");
      var right = document.getElementById("right");
      var leftHeight=parseFloat(getComputedStyle(left).height);
      var middleHeight=parseFloat(getComputedStyle(middle).height);
      var rightHeight=parseFloat(getComputedStyle(right).height);
      var maxHeight=0;
      if(leftHeight-middleHeight>0){
        maxHeight= leftHeight;
      }else{
        maxHeight= middleHeight;
      }
      if(rightHeight-maxHeight>0){
        maxHeight= rightHeight;
      }
      left.style.height =maxHeight+ "px";
      middle.style.height =maxHeight+ "px";
      right.style.height =maxHeight+ "px";
      console.log(maxHeight)  
    </script>
 
十一、五种盒子四方居中
1、4种定位四方居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>4种定位四方居中</title>
      <style>
        #mask1_1 {
          width: 100%;
          height: 100%;
          position: fixed;
          left: 0;
          top: 0;
          display: flex;
          justify-content: center;
          align-items: center;
        }
        #content1_1{
          background-color: gray;
        }
        #mask2_4 {
          width: 100%;
          height: 100%;
          position: fixed;
          left: 0;
          top: 0;
        }
        #content2_2{
          position: fixed;
          left: 50%;/* 父级宽度的50% */
          top: 50%;
          transform: translate(-50%, -50%);/* 自身宽度的50% */
          background-color: gray;
        }
        #content3_3{
          position: fixed;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          margin: auto;
          width: 400px;
          height: 300px;
          background-color: gray;
        }
        #content4_4{
          position: fixed;
          background-color: gray;
        }
      </style>
    </head>
    <body>
      <div id="mask1_1"><div id="content1_1">居中一</div></div>
      <!-- <div id="mask1_1"><div id="content1_1">居中一</div></div> -->
      <!-- <div id="mask2_4"><div id="content2_2">居中二</div></div> -->
      <!-- <div id="mask2_4"><div id="content3_3">居中三</div></div> -->
      <!-- <div id="mask2_4"><div id="content4_4">居中四</div></div> -->
    </body>
  </html>
  <script>
    var oDiv = document.getElementById('content4_4');
    if(oDiv){
      var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
      var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
      oDiv.style.left = (clientWidth - oDiv.clientWidth) / 2 + 'px';
      oDiv.style.top = (clientHeight - oDiv.clientHeight) / 2 + 'px';
    }
  </script>
2、1种无定位四方居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>CSS无定位居中</title>
      <style>
        .outer{
          width: 1000px;
          height: 500px;
          background: gray;
          text-align: center;/* 此处,左右居中 */
        }
        /* 以下,上下居中 */
        .middleShow{
          width: 200px;
          height: 100px;
          display: inline-block;
          vertical-align: middle;
          /* 以下,标识区域 */
          background: gainsboro;
        }
        .middleHide{
          width: 0;
          height: 100%;
          display: inline-block;
          vertical-align: middle;
        }
      </style>
    </head>
    <body>
      <div class="outer">
        <div class="middleShow"></div>
        <div class="middleHide"></div>
      </div>
    </body>
  </html>
3、三种横向换行也居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>3种横向换行也居中</title>
      <style>
        .center{
          display: flex;
          flex-wrap: wrap;
          flex-direction: column;
          border: 1px solid green;
          align-items: center;/* 独特之处 */
        }
        .center1{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: center;/* 独特之处 */
        }
        .center2{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: space-around;/* 独特之处 */
        }
        .center3{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: space-between;/* 独特之处 */
        }
        .center > div,.center1 > div,.center2 > div,.center3 > div{
          width: 400px;
          height: 80px;
          border: 1px solid red;
        }
      </style>
    </head>
    <body>
      <div class="center">
        <div>竖向换行也居中</div>
        <div></div>
      </div>
      <div class="center1">
        <div>横向换行也居中-1,之间无间距</div>
        <div>justify-content: center</div>
      </div>
      <div class="center2">
        <div>横向换行也居中-2,之间大间距</div>
        <div>justify-content: space-around</div>
      </div>
      <div class="center3">
        <div>横向换行也居中-3,之间超大间距</div>
        <div>justify-content: space-between</div>
      </div>
    </body>
  </html>

十二、em、rem自适应
1、em示例
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>em示例</title>
    </head>
    <body>
      <div style="font-size: 50px">
        <div style="width: 20em;height:4em;background: red;border:1px solid green;border-radius:10px;font-family:'楷体';">
          用em做单位的都是相对于父级字体大小的,本例父级字体的大小是50px,所以这里的宽20em就是1000px,高4em就是200px!
        </div>
      </div>
    </body>
  </html>
2、rem自适应示例
  (1)rem自适应示例之css版(实用程度低)
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>rem自适应示例之css版(实用程度低)</title>
        <style>
            html { font-size: 20px; }
          /* :root { font-size: 20px; } */
        </style>
      </head>
      <body>
        <div style="font-size: 1.6rem">
          <div style="width: 50rem;height:18rem;background: red;border:1px solid green;border-radius:10px;font-family:'楷体';padding-left:20px;">
            <p>根字体的大小即1rem,用rem做单位的都是相对于根字体大小的。</p>
            <p>CSS设置根字体,有2种方法,见style标签</p>
          </div>
        </div>
      </body>
    </html>
  (2)rem自适应示例之js版(实用程度高)
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>rem自适应示例之js版(实用程度高)</title>
      </head>
      <body>
        <div style="font-size: 16rem">
          <div style="width: 500rem;height:180rem;background: red;border:1px solid green;border-radius:10px;font-family:'楷体';padding-left:20px;">
            <p>根字体的大小即1rem,用rem做单位的都是相对于根字体大小的。</p>
            <p>下面把1rem定义为:(屏幕宽度/设计稿的宽度)+"px"。</p>
            <p>document.documentElement.style.fontSize=</p>
            <p>document.documentElement.clientWidth/750+"px";</p>
          </div>
        </div>
      </body>
    </html>
    <script>
      var designWidth = 1000 ;
      document.documentElement.style.fontSize = document.documentElement.clientWidth/designWidth+"px";
    </script>
 
十三、Flex布局
1、flex是2009年,W3C提出了一种布局方案,子元素的float、clear和vertical-align属性失效
  (1)一个固定,另一个占据剩余宽或高
    .box{
      width: 100%;//height: 100%
      display: flex;
      flex-direction: row;//flex-direction: column;
    }
    .box>.div1{
      width:100px;//height: 100px;
    }
    .box>.div2{
      flex:1;
    }
    height: calc(100vh - 200px)
  (2)横向按钮组居中居右
    .div{
        display: flex;
        justify-content: center; //flex-end
    }
    .div button{
        margin-left: 20px;
    }
  (3)行内元素也可以使用 Flex 布局。
    .box{
      display: inline-flex;
    }
2、flex作为属性,区别于上面作为属性值
  来源,https://blog.csdn.net/Shivy_/article/details/122361132
  来源,https://www.runoob.com/w3cnote/flex-grammar.html
    .item {
      flex: flex-grow flex-shrink flex-basis
      flex: 1 1 //可放大,可缩小
      flex: 0 0 //不放大,不缩小
    }
  (1)flex-grow,项目的放大比例,默认为0,即使存在剩余空间,也不放大;设为1,子盒子占满父盒子的剩余空间
  (2)flex-shrink,属性定义了项目的缩小比例,设为0,即使空间不足,也不缩小;默认为1,如果空间不足,该项目将缩小
  (3)flex-basis,属性定义了在分配多余空间之前,项目占据的主轴空间,默认值为auto,即项目的本来大小
3、flex-direction:主轴的方向
  .box {
    flex-direction: row | row-reverse | column | column-reverse;
  }
  (1)row(默认值):主轴为水平方向,起点在左端
  (2)row-reverse:主轴为水平方向,起点在右端
  (3)column:主轴为垂直方向,起点在上沿
  (4)column-reverse:主轴为垂直方向,起点在下沿
4、flex-wrap:主轴的换行
  .box{
    flex-wrap: nowrap | wrap | wrap-reverse;
  }
  (1)nowrap(默认):不换行
  (2)wrap:换行,第一行在上方
  (3)wrap-reverse:换行,第一行在下方
5、justify-content:主轴的对齐
  .box {
    justify-content: flex-start | flex-end | center | space-around | space-between;
  }
  具体对齐方式与轴的方向有关
  (1)flex-start(默认值):主轴起点对齐
  (2)flex-end:主轴终点对齐
  (3)center: 主轴居中对齐,之间0间距
  (4)space-around:主轴居中对齐,之间小间距,项目与外边框的间隔,是项目之间间隔的一半
  (5)space-between:主轴两端对齐,之间大间距,项目与外边框的间隔,是0
6、align-items:交叉轴对齐
  .box {
    align-items: flex-start | flex-end | center | baseline | stretch;
  }
  具体的对齐方式与交叉轴的方向有关
  (1)flex-start:交叉轴的起点对齐
  (2)flex-end:交叉轴的终点对齐
  (3)center:交叉轴的中点对齐
  (4)baseline: 项目的第一行文字的基线对齐
  (5)stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度
7、flex-flow:flex-direction和flex-wrap的简写,默认值为row nowrap
  .box {
    flex-flow: <flex-direction> || <flex-wrap>;
  }
8、align-content属性
  align-content属性定义了多根轴线的对齐方式如果项目只有一根轴线,该属性不起作用
  .box {
    align-content: flex-start | flex-end | center | space-between | space-around | stretch;
  }
  (1)flex-start:与交叉轴的起点对齐
  (2)flex-end:与交叉轴的终点对齐
  (3)center:与交叉轴的中点对齐
  (4)space-between:与交叉轴两端对齐,轴线之间的间隔平均分布
  (5)space-around:每根轴线两侧的间隔都相等所以,轴线之间的间隔比轴线与边框的间隔大一倍
  (6)stretch(默认值):轴线占满整个交叉轴

十四、多图片延迟加载下载
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        img {
          border: none;
        }
        div {
          margin: 0 auto;
          width: 800px;
          height: 400px;
          background: url("http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518060703_mthumb.jpg") no-repeat center #e1e1e1;
        }
        div img {
          width: 100%;
          height: 100%;
        }
        p {
          width: 800px;
          height: 600px;
          line-height: 60px;
          background: lightgrey;
          font-size: 30px;
          margin: 0 auto;
          text-indent: 13px;
        }
      </style>
    </head>
    <body>
      <p>
        <span>多图片延迟加载:</span><br/>
        <span>(1)多图片延迟加载;</span><br/>
        <span>(2)多图片延迟加载;</span><br/>
        <span>(3)多图片延迟加载。</span>
      </p>
    </body>
    <script>
      var data = [
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
      ];
      var aDiv = document.getElementsByTagName("div");
      var aImg = document.getElementsByTagName("img");
      function bind() {
        var frg = document.createDocumentFragment();
        for (var i = 0; i < data.length; i++) {
          var div = document.createElement("div");
          div.innerhtml = "<img realImg='" + data[i].src + "' alt=''/>";
          frg.appendChild(div);
        }
        document.body.appendChild(frg);
        frg = null;
      }
      bind();
      window.onscroll = function () {
        var scrollBottom =
          document.documentElement.scrollTop + document.documentElement.clientHeight;
        for (var i = 0; i < aDiv.length; i++) {
          var thisImg = aImg[i];
          var thisDiv = aDiv[i];
          var imgPosition = thisDiv.offsetTop + thisDiv.offsetHeight;
          if (imgPosition - 200 < scrollBottom) {
            if (thisImg.isLoad) continue;
            thisImg.src = thisImg.getAttribute("realImg");
            thisImg.isLoad = true;
          }
        }
      };
      /* 
        当相关服务关闭时,登录页仍然可见,相关图片地址的配置如下
        $timeout(function () {
          var thisImg = new Image();
          thisImg.src = $scope.r_g_company.logoPage.logo.src; 
          thisImg.onerror= function() {
            $scope.r_g_company.logoPage.logo.src = $scope.r_g_company.logoPage.logo.srcCY
          }
        },1000);
        $.get('/app_v3/oem/info?' +new Date().getTime()).then(function (res) {
          $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
        }).catch(function(){
          $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
          $scope.r_g_company.logoPage.logo.srcCY = '/audit-html/static/img/cy_tech/logo.png';
        });
      */
    </script>
  </html>
 
十五、canvas绘图
1、党徽
  <!doctype html>
  <html>
    <head>
        <meta charset="UTF-8">
        <title>党~徽</title>
    </head>
    <body>
      <canvas id="canvas" width="450" height="450">
          您的浏览器不支持canvas标签,无法看到党~徽
      </canvas>
    </body>
  </html>
  <script>
      var canvas = document.getElementById("canvas");
      var context = canvas.getContext("2d");
      context.beginPath();
      context.arc(215, 215, 200, 0, 360);
      context.fillStyle = "#030d48";
      context.strokeStyle = "grey";
      context.lineWidth = "10";
      context.stroke();
      context.fill();
      context.closePath();
      context.beginPath();
      for (var i = 0; i < 12; i++) {
          context.lineTo(Math.cos((i * 30) / 180 * Math.PI) * 200 + 215,
                  Math.sin((i * 30) / 180 * Math.PI) * 200 + 215);
          context.lineTo(Math.cos((i * 30 + 15) / 180 * Math.PI) * 105 + 215,
                  Math.sin((i * 30 + 15) / 180 * Math.PI) * 105 + 215);
      }
      context.fillStyle = "#ffffff";
      context.fill();
      context.closePath();
      context.beginPath();
      context.arc(215, 215, 105, 0, 360);
      context.strokeStyle = "#030d48";
      context.lineWidth = "10";
      context.stroke();
      context.closePath();
  </script>
2、五角星
  <!doctype html>
  <html>
    <head>
        <meta charset="UTF-8">
        <title>五~角~星</title>
    </head>
    <body>
      <canvas id="canvas" width="450" height="450">
          您的浏览器不支持canvas标签,无法看到五~角~星
      </canvas>
    </body>
  </html>
  <script>
      var canvas = document.getElementById("canvas");
      var context1 = canvas.getContext("2d");
      context1.beginPath();
      context1.rotate(18*Math.PI/180);
      for (i = 0; i < 5; i++) {
          context1.lineTo(Math.cos((i * 72+36) / 180 * Math.PI) * 200 + 250,
                  Math.sin((i * 72+36) / 180 * Math.PI) * 200 + 150);
          context1.lineTo(Math.cos((i * 72+72) / 180 * Math.PI) * 75 + 250,
                  Math.sin((i * 72+72) / 180 * Math.PI) * 75 + 150);
      }
      context1.fillStyle = "#ff0000";
      context1.fill();
      context1.closePath();
  </script>
3、时钟
  这是用HTML5的canvas制作的一个钟表,包含表盘、时针、分针、秒针及它们的运动;另外还添加了自动读时间的功能。
  <!doctype html>
  <html>
    <head>
      <style>
        #clock{
          display:block;
          background:url("") no-repeat;
          margin: 0 auto;
        }
      </style>
    </head>
    <body>
      <canvas id="clock" width="500" height="500" >
          您的浏览器不支持canvas标签,无法看到时钟
      </canvas>
    </body>
  </html>
  <script>
    var clock=document.getElementById('clock');
    var context=clock.getContext('2d');
    function drawClock() {
      context.clearRect(0, 0, 500, 500);
      function tow(n) {
          return n >= 0 && n < 10 ? '0' + n : '' + n;
      }
      var now = new Date();
      var second = now.getSeconds();
      var minute = now.getMinutes();
      var hour = now.getHours();
      var date = now.getDate();
      var month = now.getMonth()+1;
      var year = now.getFullYear();
      hour = hour + minute / 60;
      var hour1=hour;
      hour = hour > 12 ? hour - 12 : hour;
      //制作时钟外圈
      context.lineWidth = 10;
      context.beginPath();
      context.arc(250, 270, 200, 0, 360, false);
      context.stroke();
      context.closePath();
      //小时刻度制作
      for (i = 0; i < 12; i++) {
        context.save();
        context.lineWidth = 9;
        context.strokeStyle = "red";
        context.translate(250, 270);
        context.rotate(i * 30 * Math.PI / 180);
        context.beginPath();
        context.moveTo(0, -170);
        context.lineTo(0, -190);
        context.stroke();
        context.closePath();
        context.restore();
      }
      //分钟刻度制作
      for ( i = 0; i < 60; i++) {
        context.save();
        context.lineWidth = 5;
        context.strokeStyle = "blue";
        context.translate(250, 270);
        context.rotate(i * 6 * Math.PI / 180);
        context.beginPath();
        context.moveTo(0, -180);
        context.lineTo(0, -190);
        context.stroke();
        context.closePath();
        context.restore();
      }
      //钟面上表示小时的数字
      for (var i = 1; i < 13; i++) {
        context.save();
        var deg = i * Math.PI / 6;
        context.translate(250, 270);
        var x = Math.sin(deg);
        var y = -Math.cos(deg);
        context.fillStyle = "block";
        context.font = "25px 宋体";
        context.textAlign = "center";
        context.textBaseline = "middle";
        context.lineWidth=1;
        context.strokeStyle="white";
        context.beginPath();
        context.strokeText(i, x * 155, y * 155);
        context.closePath();
        context.restore();
      }
      //时针制作
      context.save();
      context.lineWidth=7;
      context.strokeStyle="#000";
      context.translate(250,270);
      context.rotate(hour*30*Math.PI/180);
      context.beginPath();
      context.moveTo(0,-110);
      context.lineTo(0,10);
      context.stroke();
      context.closePath();
      context.restore();
      //分针制作
      context.save();
      context.lineWidth=5;
      context.strokeStyle="#000";
      context.translate(250,270);
      context.rotate(minute*6*Math.PI/180);
      context.beginPath();
      context.moveTo(0,-135);
      context.lineTo(0,15);
      context.stroke();
      context.closePath();
      context.restore();
      //秒针制作
      context.save();//保存当前环境;
      //以下是秒针的主体
      context.strokeStyle="red";
      context.lineWidth=3;
      context.translate(250,270);
      context.rotate(second*6*Math.PI/180);//秒针旋转的速度
      context.beginPath();
      context.moveTo(0,-170);
      context.lineTo(0,20);
      context.stroke();
      context.closePath();
      //以上是秒针的主体,以下是时针、分针、秒针的交叉点
      context.beginPath();
      context.arc(0,0,5,0,360,false);
      context.closePath();
      context.fillStyle="gray";
      context.fill();
      context.stroke();
      //以上是时针、分针、秒针的交叉点,以下是秒针的顶端装饰。
      context.beginPath();
      context.arc(0,-150,5,0,360,false);
      context.closePath();
      context.fillStyle="gray";
      context.fill();
      context.stroke();
      context.restore();//返回已保存过的环境。
      //以下是文字报时;
      context.save();
      context.font="23px 楷体";
      context.lineWidth=1;
      context.strokeStyle="white";
      context.strokeText("现在是北京时间:"+tow(year)+"年"+tow(month)+"月"+tow(date)+"日 "
              +tow(Math.floor(hour1))+"时"+tow(minute)+"分"+tow(second)+"秒",5,30);
      context.restore();
    }
    drawClock();
    setInterval(drawClock,1000);
  </script>
 
十六、弹窗
1、弹窗-系统内置
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
    </head>
    <body style="font-size: 30px;"> 
      <pre>     
        alert:
          1、弹-窗有确定按钮,阻塞线程,
          2、alert后面的代码,点击确认后才执行
        confirm:
          1、弹-窗有两个按钮(确定和取消),阻塞线程,
          2、confirm后面的代码,点击按钮后才执行,
          3、点击确定返回true,点击取消返回false
        prompt:
          1、弹-窗有一个输入框、两个按钮(确定和取消),阻塞线程
          2、prompt后面的代码,点击按钮后才执行,
          3、点击确定返回输入内容,为空返回空字符串,点击取消返回null
      </pre>
      <input type="button" onclick="fnAlert()" value="显示alert框" />
      <input type="button" onclick="fnConfirm()" value="显示confirm框" />
      <input type="button" onclick="fnPrompt()" value="显示prompt框" />
    </body>
  </html>
  <script>
    function fnAlert(){
      var text = alert("你好,我是一个alert框!");
      console.log( text );
    }
    function fnConfirm(){
      var text = confirm("你好,我是一个confirm框!");
      console.log( text );
    }
    function fnPrompt(){
      var text = prompt("你好,我是一个prompt框!","这是默认输入!");
      console.log( text );
    }
  </script>
2、弹窗-手写(可拖拽和缩放)
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>弹窗-可拖拽和缩放</title>
      <style>
        * {
          padding: 0;
          margin: 0;
        }
        html,body {
          height: 100%;
        }
        .mask {
          position: fixed;
          width: 100%;
          height: 100%;
          background: #bbb;
        }
        #alert {
          position: absolute;
          background: #ddd;
          margin: auto;
          width: 600px;
          height: 800px;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          user-select: none;
          resize: both;
          overflow: auto;
        }
        .innerBox {
          padding: 0 30px;
        }
        p {
          line-height: 40px;
        }
        .title {
          height: 50px;
          line-height: 50px;
          background: gray;
          padding-left: 30px;
          cursor: pointer;
        }
        .submit{
          text-align: center;
          background: gray;
          display: block;
          margin: 0 auto;
          width:100px;
          height:30px;
          line-height: 30px;
        }
      </style>
    </head>
    <body>
      <div class="mask">
        <div id="alert">
          <p id="alert-title-id" class="title"> 本弹窗特征(鼠标置于此处,方可拖动): </p>
          <div class="innerBox">
            <p> 1、标题区可拖拽 </p>
            <p> 2、内容区可以是任意高度、宽度 </p>
            <p> 3、初始位置居中,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> left: 50%; </p>
              <p> top: 50%;</p>
              <p> transform: translate(-50%, -50%);</p>
            </div>
            <p> 4、弹-窗可缩放,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> resize: both; </p>
              <p> overflow: auto;</p>
            </div>
            <p> 5、提交按钮和文字居中,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> text-align:center; width:100px; background:gray;</p>
              <p> display:block; height:30px; line-height:30px; margin:0 auto;</p>
            </div>
            <p> 6、你使用时,在关闭弹-窗之前,用上面3处css代码重置弹-窗的位置,否则,下次使用弹-窗时,弹-窗将出现在上次关闭时的地方。 </p>
            <p> 7、弹-窗向任何方向(上下左右)拖拽,当消失3/4时,停止移动。 </p>
            <p> 8、拖拽弹-窗的右下方,可以实现缩放。 </p>
          </div>
          <div style="padding-top:30px;">
            <span class="submit">提交</span>
          </div>
        </div>
      </div>
    </body>
  </html>
  <script>
    var oDiv = document.getElementById("alert");
    oDiv.onmousedown = down;
    function processThis(fn, currentThis) {
      return function (event) {
        fn.call(currentThis, event); //”先触发,后执行“与”先执行,后触发“
      };
    }
    function down(event) {
      event = event || window.event;
      if (event.target.id != "alert-title-id") return;
      this.initOffsetLeft = this.offsetLeft;
      this.initOffsetTop = this.offsetTop;
      this.initClientX = event.clientX;
      this.initClientY = event.clientY;
      this.maxOffsetWidth =
        (document.documentElement.clientWidth || document.body.clientWidth) -
        this.offsetWidth;
      this.maxOffsetHeight =
        (document.documentElement.clientHeight || document.body.clientHeight) -
        this.offsetHeight;
      if (this.setCapture) {
        this.setCapture();
        this.onmousemove = processThis(move, this);
        this.onmouseup = processThis(up, this);
      } else {
        document.onmousemove = processThis(move, this);
        document.onmouseup = processThis(up, this);
      }
    }
    function move(event) {
      var currentLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
      var currentTop = this.initOffsetTop + (event.clientY - this.initClientY);
      //以下都是边界值的判断;弹窗向任何方向(上下左右)拖拽,当消失3/4时,停止移动。
      if (currentLeft > this.maxOffsetWidth + this.clientWidth / 0.8) {
        currentLeft = this.maxOffsetWidth + this.clientWidth / 0.8;
      } else if (currentLeft < -this.clientWidth / 4) {
        currentLeft = -this.clientWidth / 4;
      }
      if (currentTop > this.maxOffsetHeight + this.clientHeight / 0.8) {
        currentTop = this.maxOffsetHeight + this.clientHeight / 0.8;
      } else if (currentTop < 300) {
        //-this.clientHeight / 4
        currentTop = 300; //-this.clientHeight / 4
      }
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失3/4时,停止移动。
      //以下都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失1/2时,停止移动。
      /* if (currentLeft > this.maxOffsetWidth + this.clientWidth) {
          currentLeft = this.maxOffsetWidth + this.clientWidth;
        } else if (currentLeft < -this.clientWidth / 64) {
          currentLeft = -this.clientWidth / 64;
        }
        if (currentTop > this.maxOffsetHeight + this.clientHeight) {
          currentTop = this.maxOffsetHeight + this.clientHeight;
        } else if (currentTop < -this.clientHeight / 64) {
          currentTop = -this.clientHeight / 64;
        } */
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失1/2时,停止移动。
      //以下都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,触边时,停止移动。
      /* if (currentLeft > this.maxOffsetWidth + this.clientWidth / 2) {
          currentLeft = this.maxOffsetWidth + this.clientWidth / 2;
        } else if (currentLeft < this.clientWidth / 2) {
          currentLeft = this.clientWidth / 2;
        }
        if (currentTop > this.maxOffsetHeight + this.clientHeight / 2) {
          currentTop = this.maxOffsetHeight + this.clientHeight / 2;
        } else if (currentTop < this.clientHeight / 2) {
          currentTop = this.clientHeight / 2;
        } */
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,触边时,停止移动。
      this.style.left = currentLeft + "px";
      this.style.top = currentTop + "px";
      console.log(this.style.left);
      console.log(this.style.top);
    }
    function up() {
      if (this.releaseCapture) {
        this.releaseCapture();
        this.onmousemove = null;
        this.onmouseup = null;
      } else {
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
  </script>
  
十七、表格排序 
  说明,这里的表格排序,包含按姓名、年龄、分数、性别等汉字和数字的排序。用纯原生JavaScript代码实现,同时还实现了隔行变色。
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        #table {
          width: 600px;
          border: 3px solid darkgreen;
          margin: 20px auto;
          text-align: center;
        }
        #table tr {
          height: 40px;
          line-height: 40px;
        }
        .bg0 {
          background: mediumvioletred;
        }
        .bg1 {
          background: greenyellow;
        }
        .bg2 {
          background: yellow;
        }
        .cursor {
          cursor: pointer
        }
      </style>
    </head>
    <body>
      <table id="table" class="table">
        <thead>
          <tr class="bg2">
            <th class="cursor">姓名</th>
            <th class="cursor">年龄</th>
            <th class="cursor">分数</th>
            <th class="cursor">性别</th>
          </tr>
        </thead>
        <tbody>
        </tbody>
      </table>
    </body>
  </html>
  <script>
    var table = document.getElementById("table");
    var th = table.tHead.rows[0].cells;
    var body = table.tBodies[0];
    var row = body.rows;
    console.log(row); 
    console.log('上面,展开前是空数组,这是代码执行到此的结果'); 
    console.log('上面,展开后有6项数据,这是页面渲染完毕后的结果'); 
    var data = [
      { name: "赵老大", age: 45, score: 60, sex: 0 },
      { name: "钱老二", age: 24, score: 67, sex: 1 },
      { name: "孙老三", age: 38, score: 79, sex: 1 },
      { name: "李老四", age: 30, score: 80, sex: 0 },
      { name: "周老五", age: 65, score: 56, sex: 1 },
      { name: "吴老六", age: 26, score: 26, sex: 0 },
    ];
    //绑定原始数据
    bind();
    function bind() {
      var frg = document.createDocumentFragment();
      for (var i = 0; i < data.length; i++) {
        var cur = data[i];
        var tr = document.createElement("tr");
        for (var attr in cur) {
          if (attr === "sex") {
            cur[attr] = cur[attr] === 0 ? "男" : "女";
          }
          var td = document.createElement("td");
          td.innerHTML = cur[attr];
          tr.appendChild(td);
        }
        frg.appendChild(tr);
      }
      body.appendChild(frg); //2、
      frg = null;
    }
    //实现隔行变色
    changeColor();
    function changeColor() {
      for (var i = 0; i < row.length; i++) {
        row[i].className = "bg" + (i % 2);
      }
    }
    //绑定点击事件
    for (var i = 0; i < th.length; i++) {
      if (th[i].className === "cursor") {
        th[i].flag = -1;
        th[i].index = i;
        th[i].onclick = function () {
          sortArray.call(this, this.index);
        };
      }
    }
    //类数组转化为数组
    function makeArray(arg) {
      var ary = [];
      try {
        ary = Array.prototype.slice.call(arg);
      } catch (e) {
        for (var i = 0; i < arg.length; i++) {
          ary.push(arg[i]);
        }
      }
      return ary;
    }
    //点击事件中的排序
    function sortArray(n) {
      var that = this;
      for (var i = 0; i < th.length; i++) {
        th[i].flag = i === n ? (that.flag *= -1) : -1;
      }
      var ary = makeArray(row);
      ary.sort(function (rowBefore, rowBehind) {
        var rowInnerBefore = rowBefore.cells[n].innerHTML;
        var rowInnerBehind = rowBehind.cells[n].innerHTML;
        return rowInnerBefore.localeCompare(rowInnerBehind);
      });
      var frg = null;
      for (i = 0; i < ary.length; i++) {
        frg = ary[i];
        body.appendChild(frg);
      }
      changeColor();
    }
  </script>

十八、拖拽
1、拖拽-普通
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>拖拽-普通</title>
      <style>
        *{
          margin:0;
          padding:0;
        }
        div{
          position: absolute;
          left:0;
          top:0;
          width: 100px;
          height: 100px;
          background: red;
        }
      </style>
    </head>
    <body>
      <div id="div"></div>
    </body>
  </html>
  <script>
    var oDiv = document.getElementById("div");
    oDiv.onmousedown = down;
    function processThis(fn, obj) {
      return function (e) {
        fn.call(obj, e);
      };
    }
    function down(event) {
      event = event || window.event;
      this.offsetLeftPass = this.offsetLeft;
      this.offsetTopPass = this.offsetTop;
      this.eventClientX = event.clientX;
      this.eventClientY = event.clientY;
      if (this.setCapture) {
        this.setCapture();
        this.onmousemove = processThis(move, this);
        this.onmouseup = processThis(up, this);
      } else {
        document.onmousemove = processThis(move, this);
        document.onmouseup = processThis(up, this);
      }
    }
    function move(event) {
      event = event || window.event;
      this.style.left =
        this.offsetLeftPass + (event.clientX - this.eventClientX) + "px";
      //this.offsetLeftPass:移动前offsetLeft值;(event.clientX-this.eventClientX):鼠标横向移动的距离,即盒子横向移动的距离
      this.style.top =
        this.offsetTopPass + (event.clientY - this.eventClientY) + "px";
      //this.offsetTopPass:移动前offsetTop值;(event.clientX-this.eventClientX):鼠标纵向移动的距离,即盒子纵向移动的距离
    }
    function up() {
      if (this.releaseCapture) {
        this.releaseCapture();
        this.onmousemove = null;
        this.onmouseup = null;
      } else {
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
  </script>
2、拖拽-zTree
  <!DOCTYPE html>
  <html>
    <head>
      <title>拖拽-zTree</title>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" href="https://cdn.bootcss.com/zTree.v3/3.5.33/css/zTreeStyle/zTreeStyle.min.css">
      <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdn.bootcss.com/zTree.v3/3.5.33/js/jquery.ztree.core.min.js"></script>
      <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/zTree.v3/3.5.29/js/jquery.ztree.excheck.js"></script>
      <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/zTree.v3/3.5.29/js/jquery.ztree.exedit.js"></script>
      <script type="text/javascript">
        var setting = {
          edit: {
            enable: true,
            showRemoveBtn: false,
            showRenameBtn: false
          },
          data: {
            simpleData: {
              enable: true
            }
          },
          callback: {
            beforeDrag: beforeDrag,
            beforeDrop: beforeDrop
          }
        };
        var zNodes =[
          { id:1, pId:0, name:"随意拖-拽 1", open:true},
          { id:11, pId:1, name:"随意拖-拽 1-1"},
          { id:12, pId:1, name:"随意拖-拽 1-2", open:true},
          { id:121, pId:12, name:"随意拖-拽 1-2-1"},
          { id:122, pId:12, name:"随意拖-拽 1-2-2"},
          { id:123, pId:12, name:"随意拖-拽 1-2-3"},
          { id:13, pId:1, name:"禁止拖-拽 1-3", open:true, drag:false},
          { id:131, pId:13, name:"禁止拖-拽 1-3-1", drag:false},
          { id:132, pId:13, name:"禁止拖-拽 1-3-2", drag:false},
          { id:133, pId:13, name:"随意拖-拽 1-3-3"},
          { id:2, pId:0, name:"随意拖-拽 2", open:true},
          { id:21, pId:2, name:"随意拖-拽 2-1"},
          { id:22, pId:2, name:"禁止拖-拽到我身上 2-2", open:true, drop:false},
          { id:221, pId:22, name:"随意拖-拽 2-2-1"},
          { id:222, pId:22, name:"随意拖-拽 2-2-2"},
          { id:223, pId:22, name:"随意拖-拽 2-2-3"},
          { id:23, pId:2, name:"随意拖-拽 2-3"}
        ];
        function beforeDrag(treeId, treeNodes) {
          for (var i=0,l=treeNodes.length; i<l; i++) {
            if (treeNodes[i].drag === false) {
              return false;
            }
          }
          return true;
        }
        function beforeDrop(treeId, treeNodes, targetNode, moveType) {
          return targetNode ? targetNode.drop !== false : true;
        }
        function setCheck() {
          var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
          isCopy = $("#copy").attr("checked"),
          isMove = $("#move").attr("checked"),
          prev = $("#prev").attr("checked"),
          inner = $("#inner").attr("checked"),
          next = $("#next").attr("checked");
          zTree.setting.edit.drag.isCopy = isCopy;
          zTree.setting.edit.drag.isMove = isMove;
          showCode(1, ['setting.edit.drag.isCopy = ' + isCopy, 'setting.edit.drag.isMove = ' + isMove]);
          zTree.setting.edit.drag.prev = prev;
          zTree.setting.edit.drag.inner = inner;
          zTree.setting.edit.drag.next = next;
          showCode(2, ['setting.edit.drag.prev = ' + prev, 'setting.edit.drag.inner = ' + inner, 'setting.edit.drag.next = ' + next]);
        }
        function showCode(id, str) {
          var code = $("#code" + id);
          code.empty();
          for (var i=0, l=str.length; i<l; i++) {
            code.append("<li>"+str[i]+"</li>");
          }
        }
        $(document).ready(function(){
          $.fn.zTree.init($("#treeDemo"), setting, zNodes);
          setCheck();
          $("#copy").bind("change", setCheck);
          $("#move").bind("change", setCheck);
          $("#prev").bind("change", setCheck);
          $("#inner").bind("change", setCheck);
          $("#next").bind("change", setCheck);
        });
      </script>
    </head>
    <body>
      <p>参考文档1,https://treejs.cn/v3/main.php#_zTreeInfo</p>
      <p>参考文档2,https://www.bootcdn.cn/zTree.v3/3.5.29</p>
      <div class="content_wrap">
        <div class="zTreeDemoBackground left">
          <ul id="treeDemo" class="ztree"></ul>
        </div>
        <div class="right">
          <ul class="info">
            <li class="title"><h2>1、setting 配置信息说明</h2>
              <ul class="list">
              <li>此 Demo 仅从功能上演示实现拖拽的基本方法和配置参数</li>
              <li class="highlight_red">1)、使用拖-拽功能,必须设置 setting.edit 中的各个属性,详细请参见 API 文档中的相关内容</li>
              <li class="highlight_red">2)、使用拖-拽功能的事件回调函数,必须设置 setting.callback.beforeDrag / onDrag / beforeDrop / onDrop 等属性,详细请参见 API 文档中的相关内容</li>
              <li><p>基本拖-拽设置:<br/>
                  <input type="checkbox" id="copy" class="checkbox first" checked /><span>允许复制</span>
                  <input type="checkbox" id="move" class="checkbox " checked /><span>允许移动</span><br/>
                  <ul id="code1" class="log" style="height:42px;"></ul></p>
              </li>
              <li><p>拖-拽相对位置设置:<br/>
                  <input type="checkbox" id="prev" class="checkbox first" checked /><span>prev</span>
                  <input type="checkbox" id="inner" class="checkbox " checked /><span>inner</span>
                  <input type="checkbox" id="next" class="checkbox " checked /><span>next</span><br/>
                  <ul id="code2" class="log" style="height:65px;"></ul></p>
              </li>
              </ul>
            </li>
            <li class="title"><h2>2、treeNode节点数据说明</h2>
              <ul class="list">
              <li>对节点数据没有特殊要求,用户可以根据自己的需求添加自定义属性</li>
              </ul>
            </li>
          </ul>
        </div>
      </div>
    </body>
  </html>
 

  

posted @ 2019-06-03 14:04  WEB前端工程师_钱成  阅读(4131)  评论(0编辑  收藏  举报