美文网首页
DOM (Document Object Model)

DOM (Document Object Model)

作者: 啃香菜的花萝萝 | 来源:发表于2019-04-27 20:08 被阅读0次

    DOM是一套编程接口,让js能操作html和xml。

    1. DOM选择器

    1.1 document.getElementById()

    • 其中元素id在ie8以下的浏览器不区分大小写
    • 该方法定义在 Document.prototype上,即Element节点上不能使用

    1.2 getElementsByTagName()

    • 该方法定义在 Document.prototype 和 Element.prototype上, 最常用的一个方法
    <div>
      <span>sun</span>
    </div>
    <span></span>
    
    <script>
      var div = document.getElementsByTagName('div')[0];
      var span = div.getElementsByTagName('span')[0];
    </script>
    
    getElementsByTagName()

    1.3 getElementsByName()

    • 其中只有部分标签的name可生效,例如 表单,表单元素, img, iframe...
    • 方法定义在 HTMLDocument.prototype上

    1.4 getElementsByClassName()

    • 类名,其中ie8和ie8以下的ie版本中没有
    • 该方法定义在 Document.prototype 和 Element.prototype上

    1.5 querySelector()querySelectorAll()

    • css选择器,在ie7和ie7以下的版本中没有
    • querySelector 选出来的是一个, querySelectorAll 选出来是一组。
    • 这俩方法选出来的不是实时的。
    • 这俩方法定义在 Document.prototype 和 Element.prototype上
    <div>
      <span>
        <strong class="demo"><strong>
      </span>
    </div>
    
    <script>
      var strong = document.querySelector('div > span strong.demo')
    </script>
    

    2. 遍历节点树

    • parentNode 父节点(最顶端的parentNode为 #document)
    <div>
      <strong></strong>
      <span></span>
      <em></em>
    </div>
    
    <script>
      var strong = document.getElementsByTagName('strong')[0];
    </script>
    
    parentNode
    • childNodes 选出子节点们
    <div>
      <strong>
        <span></span>
      </strong>
      <span></span>
      <em></em>
    </div>
    
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    childNodes

    需要注意的是,这个东西选出来的子节点们,是会把所有类型的子节点都选出来。什么 文本节点,注释节点,元素节点... ...(节点的类型在后文会有写)

    • firstChild 第一个子节点
    • lastChild 最后一个子节点, 即能选择元素中的最后一个节点。
    • nextSibling & previousSibling 后/ 前一个兄弟节点
    <div>
      <strong>
        <span></span>
      </strong>
      <span></span>
      <em></em>
    </div>
    <script>
      var strong = document.getElementsByTagName('strong')[0];
    </script>
    
    nextSibling 可以看到 strong的下一个兄弟节点是文本节点,再下一个兄弟节点是 span ... ...

    3. 基于元素节点树的遍历

    下面这些方法遍历时,是基于真正的元素节点;即文本节点,注释节点等等都不会识别了。
    注意: 下面的 ie不兼容 指的是 ie9 和 ie9 以下的。

    • parentElement 返回当前元素的 父元素的节点(ie不兼容)
    <div>
      sun
      <!-- 嘤嘤嘤 -->
      <strong></strong>
      <span></span>
    </div>
    <script>
      var strong = document.getElementsByTagName('strong')[0];
    </script>
    

    需要明白的是,document自成一个节点,不是元素节点。也就是 parentElement 和 上面那个parentNode出来结果不一样的地方。

    parentElement
    • children 只返回当前元素的元素子节点们
    <div>
      <!-- 嘤嘤嘤 -->
      <strong></strong>
      <span></span>
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    children
    • node.childElementCount === node.children.length 当前元素节点的子元素节点个数 (ie不兼容)
    • firstElementChild & lastElementChild 返回的是第一个/ 最后一个元素子节点 (ie不兼容)
    <div>
      <strong></strong>
      <span></span>
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    firstElementChild/ lastElementChild
    • nextElementSibling / previousElementSibling 返回后一个/ 前一个兄弟元素节点

    3.1 节点的四个属性

    • nodeName
      元素的标签名,返回一个字符串;只读。
    <div>
       sun
      <!--嘤嘤嘤-->
      <strong></strong>
      <span></span>
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    nodeName
    • nodeValue
      只有 Text (文本)节点 和 Comment (注释)节点有这个属性;可读写;

    • nodeType (重点)
      该节点的类型;只读。
      获取节点类型

      nodeType
    • attributes
      元素的Element 节点的属性集合

    3.2 节点的类型

    • 1 --- 元素节点
    • 2 --- 属性节点
    • 3 --- 文本节点
    • 8 --- 注释节点
    • 9 --- 文档节点 (document)

    3.4 节点的一个方法Node.hasChildNodes()
    返回值: true / false

    <div>
      <strong></strong>
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    hasChildNodes()
    注意: 是找有没有节点,不是有没有元素节点,所以下列两种情况皆返回 true
    <!--这坨div中含有注释节点-->
    <div>
      <!--嘤嘤嘤-->
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    
    <!--这坨div中含有文本节点-->
    <div>
    </div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    

    下面这种情况返回 false:

    <div></div>
    <script>
      var div= document.getElementsByTagName('div')[0];
    </script>
    

    4. DOM结构树 (表示一种继承关系)

    DOM结构树
    • 继承关系:
      document ----> HTMLDocument.prototype ----> Document.prototype
      继承关系

    即: HTMLDocument.prototype = { __proto__: Document.prototype}

    • HTMLDocument.prototype 定义了一些常用的属性, body, head 分别指代 HTML 文档中的 <body><head>标签; 用法: document.bodydocument.head
    • Document.prototype 上定义了 documentElement 属性,指代文档的根元素,在HTML文档中,他总是指代 <html> 元素;用法:document.documentElement

    5. DOM 基本操作

    5.1 增

    • document.createElement();
      创建元素节点: document.createElement('div');
    • document.createTextNode();
      创建文本节点: document.createTextNode('啦啦啦,德玛西亚');
    • document.createComment();
      创建注释节点: document.createComment('这是注释嗷');
    • document.createDocumentFragment();
      创建文档碎片节点

    5.2 插

    • PARENTNODE.appendChild();
      任何一个元素节点都有这个方法。
    <body>
        <script>
            var div = document.createElement('div');
            document.body.appendChild(div);
        </script>
    </body>
    
    appendChild()

    如果把页面中已经存在的一个部分,appendChild去另一个部分,做的是 剪切 操作。

    <body>
        <div></div>
        <span></span>
    
        <script>
            var div = document.getElementsByTagName('div')[0];
            var span = document.getElementsByTagName('span')[0];
            div.appendChild(span);
        </script>
    </body>
    
    效果
    • ParentNode.insertBefore(a, b);
      必须父级去调用,然后往父级里面放东西。(把 a 放到 b 前面)
    <body>
        <div>
            <span></span>
        </div>
       
        <script>
            var div = document.getElementsByTagName('div')[0];
            var span = document.getElementsByTagName('span')[0];
            var strong = document.createElement('strong');
            div.insertBefore(strong, span);
        </script>
    </body>
    
    效果

    5.3 删

    • parent.removeChild();
      父节点删除子节点, 该方法返回被删除的子节点
    <body>
        <div>
            <span></span>
            <i></i>
            <strong></strong>
        </div>
       
        <script>
            var div = document.getElementsByTagName('div')[0];
            var i = document.getElementsByTagName('i')[0];
            div.removeChild(i);
        </script>
    </body>
    
    效果
    • child.remove();
      自己销毁自己,返回 undefined
    <body>
        <div>
            <span></span>
            <i></i>
            <strong></strong>
        </div>
       
        <script>
            var div = document.getElementsByTagName('div')[0];
            var span = document.getElementsByTagName('span')[0];
            span.remove();
        </script>
    </body>
    
    效果

    5.4 替换

    • parent.replaceChild(new, origin);
      拿新的元素(new)去替换旧的元素(origin)
    <body>
        <div>
            <span></span>
            <i></i>
            <strong></strong>
        </div>
       
        <script>
            var div = document.getElementsByTagName('div')[0];
            var span = document.getElementsByTagName('span')[0];
            var p = document.createElement('p'); 
        </script>
    </body>
    
    Example

    5.4 Element 节点的一些属性

    • innerHTML
      改变一个元素里面的html内容
    <body>
        <div>
            <span>sun</span>
            <p>sweet</p>
        </div>
       
        <script>
            var div = document.getElementsByTagName('div')[0];
        </script>
    </body>
    
    Example
    • innerText
      取一个元素中的文本内容。


      Example

    5.5 Element 节点的一些方法

    • ele.setAttribute();
    <body>
        <div></div>
        <script>
            var div = document.getElementsByTagName('div')[0];
            div.setAttribute('class', 'test');
        </script>
    </body>
    
    效果
    • ele.getAttribute();


      效果

    6. date对象

    日期对象 Date()。打印当前是何年何月何日何时,几分几秒。

    var date = new Date();
    
    Result
    1. getDate()
      这个方法会返回今天是这个月中的第几天 (今天是5月1)


      getDate()
    2. getDay()
      这个方法会返回今天是一周中的第几天(0~6)

      注意: 周天是第一天 getDay()
    3. getMonth()
      这个方法会返回当前是几月(0 ~11)

      注意: 从0开始计算,1月为0 getMonth()
    4. getFullYear()

      这个方法会返回当前是几几年 getFullYear()
    5. getHours()/ getMinutes()/ getSeconds()
      返回Date对象的小时/分钟/秒数

      注意:这个返回的是创建时候那个时刻的时分秒 (我电脑的时间是 14:01) result
    6. getTime() 重点嗷~~~

    • 这个方法返回1970年1月1日至今的毫秒数;
    • 这个方法可以求时间戳
      下面这段代码可以求证1000000次循环用了多少时间。结果显示为 4毫秒。
    <script>
      var firstTime = new Date().getTime();
      for(var i=0; i<1000000; i++) {}  
      var lastTime = new Date().getTime();
      console.log(lastTime - firstTime);
    </script>
    
    getTime()
    1. setDate()

      这个方法可以设置Date对象中 月的某一天(1~31) setDate()
    2. toString()
      把 Date对象转换为字符串


      toString()

    7. js定时器

    注意:都是全局对象 window 上的方法,内部函数 this 指向 window。

    1. setInterval()
    • 每隔1000毫秒就执行一次函数
    • 它会返回一个数字作为它的唯一表示 (表示这是第几个定时器)
    var timer = setInterval(function() {
      console.log('sun');
    }, 1000) ;
    
    1. setTimeout()
    • 推时作用, 推迟多少时间之后才执行某个操作, 并且只执行一次。
    var timer = setTimeout(function () {
      console.log('sun');
    }, 1000);
    
    1. clearInterval()
      清除定时器
    var i = 0;
    var timer = setInterval(function() {
      console.log(i ++);
      if (i > 10) {
        clearInterval(timer);
      }
    }, 10) ;
    
    1. clearTimeout()
    var timer = setTimeout(function() {
      console.log('a');
    }, 1000);
    clearTimeout(timer);
    

    8. 用dom获取界面尺寸

    1. 查看滚动条的滚动距离
    语法: window.pageXOffset/ pageYOffset (IE及IE8以下不兼容)
    语法: document.body/documentElement.scrollLeft/ scrollTop(IE8及IE8以下用这个方法去求滚动条的滚动距离)
    注意: IE8和IE8以下以下的浏览器, document.body.scrollLeft/Topdocument.documentElement.scrollLeft/Top这两个由于兼容性问题,只有一个会生效。所以说以后用的话直接把两个加一加。

    document.body.scrollLeft + document.documentElement.scrollLeft
    
    result

    封装一个方法,使滚动条的滚动距离能兼容所有浏览器

    function getScrollOffset() {
      if(window.pageXOffset) {
        return {
          x: window.pageXOffset,
          y: window.pageYOffset
        }
      } else {
          return {
            x: document.body.scrollLeft + document.documentElement.scrollLeft,
            y: document.body.scrollTop + document.documentElement.scrollTop
          }
        }
    }
    
    效果

    2. 查看可视区窗口的尺寸
    可视区窗口其实就是 html 文档的部分。
    语法:window.innerWidth/innerHeight (IE8及IE8以下不兼容)
    语法:document.documentElement.clientWidth/clientHeight (标准模式下,任意浏览器都兼容; IE8及IE8以下是可以兼容的)
    语法: document.body.clientWidth/clientHeight (怪异/ 混杂模式模式下; IE8及IE8以下是可以兼容的)

    科普:混杂模式启用方法
    <!DOCTYPE html> 这句话删了就启用了混杂模式了。写了这句话就是标准模式。
    封装一个方法,获取可视区窗口的尺寸,兼容所有浏览器

    function getViewportOffset() {
      if(window.innerWidth) {
        return {
          w: window.innerWidth,
          h: window.innerHeight
        }
      }  else {
          if (document.compatMode === "BackCompat") {
            return {
              w: document.body.clientWidth,
              h: document.body.clientHeight
            }
          } else {
            return {
              w: document.documentElement.clientWidth,
              h: document.documentElement.clientHeight
            }
          }
       }
    }
    

    3. 查看元素的几何尺寸
    该方法兼容性很好。 任何一个dom元素都有这个方法。
    语法: domEle.getBoundingClientRect()
    该方法返回一个对象。需要注意的是,这个返回的结果并不是实时的。(讲道理,这个方法没什么用,稍微看一看。)

    <body>
        <div style="width: 50px; height: 50px; background-color: pink; 
        position: absolute; left: 100px; top: 100px;"></div>
        <script>
            var div = document.getElementsByTagName('div')[0];
        </script>
    </body>
    
    效果

    4. 查看元素尺寸
    语法: dom.offsetWidth, dom.offsetHeight

    查看元素尺寸

    5. 查看元素的位置
    语法:dom.offsetLeftdom.offsetTop
    注意:这个方法会忽略元素自身是否是定位元素,求得值是距离它有定位的父级的坐标。如果对于无定位父级的元素,则返回的值是相对文档的坐标。
    语法:dom.offsetParent
    该方法能求出相对于最近的有定位的父级,如果没有,就返回body

    6. 让滚动条滚动
    window上有三个方法:

    • scroll() / scrollTo() 这俩方法没区别,喜欢用哪个就用哪个吧。滚动到指定的点。
      里面可以填俩参数,第一个参数是让x轴滚动的距离,第二个参数是让y轴滚动的距离
    <div style="width: 5000px; height: 5000px; 
    border: 1px solid black"></div>
    
    效果
    • scroll By()
      累加滚动距离,用法和上面类似,只不过这个可以累加。

    9. 脚本化 CSS

    1. 读写元素css属性
    • dom.style.prop
      这个可以读写 行间样式
      注意:
    • 在js访问css属性时,不能访问有-的属性,例如 background-color。所以但凡碰到这样的,改成小驼峰式就可以访问了。即 backgroundColor
    • 碰到关键字的时候,例如 float,写成 cssFloat
    • 写入的值必须是字符串格式
    <body>
      <div style="width: 50px; height: 50px; background-color:pink;"></div>
      <script>
        var div = document.getElementsByTagName('div')[0];
        div.style.backgroundColor = 'red';
        div.style.width = '200px';
      </script>
    </body>
    
    效果
    1. 查询计算样式
      获取的是当前元素所展示出的一切css的显示值
    • window.getComputedStyle()
    • 第一个参数填你要获取的是哪个元素,第二个参数一般情况下填null,这个参数可以获取伪元素的样式表 例子
    • 这个方法只读,不能改。
    • 该方法返回的值都是绝对值,没有相对单位
    • IE8及IE8以下不兼容

    所以IE8和IE8以下的浏览器,查询样式使用的方法如下:

    • window.currentStyle['prop']
    • 计算样式只读
    • IE独有属性

    封装兼容性方法获取css style

    function getStyle(elem, prop) {
      if(window.getComputedStyle) {
        return window.getComputedStyle(elem, null)[prop];
      } else {
        return elem.currentStyle[prop];
      }
    }
    

    10. 事件

    1. 绑定事件的几种方法

    • ele.onxxx = function (event) {}
      兼容性很好,但是一个元素的同一个事件上只能绑定一个处理程序。基本等同于写在html行间上。
      程序this指向dom元素本身。
    <div></div>
    ... ...
    var div = document.getElementsByTagName('div')[0];
    div.onclick = function() {
      this.style.backgroundColor = 'green';
    }
    
    • obj.addEventListener(type, fn, false);
      IE9以下不兼容,可以为一个事件绑定多个处理程序。
      程序this指向dom元素本身。
    div.addEventListener('click', function() {
      
    } , false);
    
    
    • obj.attachEvent('on' + type, fn)
      IE独有,一个事件同样可以绑定多个处理程序。
      程序this指向window。
      如果想让this指向div,方式如下:
    div.attachEvent('onclick', function() {
      handle.call(div);
    })
    function handle() {
      this.xxx = xxxxxxxxx;
    }
    

    封装兼容性方法 addEvent(elem, type, handle)

    function addEvent(elem, type, handle) {
      if (elem.addEventListener) {
        elem.addEventListener(type, handle, false);
      } else if(elem.attachEvent) {
        elem.attachEvent('on' + type, function () {
          handle.call(elem);
        })
      } else {
        elem['on' + type] = handle;
      }
    }
    

    2. 解除事件处理程序
    注意:如果绑定的是匿名函数,则无法解除。

    • ele.onclick = false/''/null;
    var div = document.getElementsByTagName('div')[0];
    // 只执行一次事件
    div.onclick = function() {
      console.log('a');
      this.onclick = null;
    }
    
    • ele.removeEventListener(type, fn, false);
    var div = document.getElementsByTagName('div')[0];
    function test() {
      console.log('sun');
    }
    div.addEventListener('click', test, false);
    div.removeEventListener('click', test, false);
    
    • ele.detachEvent('on' + type, fn)

    11. 事件处理模型

    一个对象的一个事件类型只能遵循一种模型。
    如果一个对象的一个事件类型上绑定了两个函数,两个函数分别遵循捕获和冒泡,那么执行顺序为 先捕获后冒泡

    1. 事件冒泡
    • 结构上嵌套关系(就是父子关系)的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(自底向上)
    • 特殊事件没有冒泡功能: focus, blur, change, submit, reset, select
    <style>
      .wrapper{
        width: 300px;
        height: 300px;
        background-color: red;
      }
      .content {
        width: 200px;
        height: 200px;
        background-color: green;
      }
      .box {
        width: 100px;
        height: 100px;
        background-color: yellow;
      }
    </style>
    ... ...
    <body>
        <div class="wrapper">
            <div class="content">
                <div class="box"></div>
            </div>
        </div>
        
        <script>
            var wrapper = document.getElementsByClassName('wrapper')[0];
            var content = document.getElementsByClassName('content')[0];
            var box = document.getElementsByClassName('box')[0];
    
            wrapper.addEventListener('click', function() {
                console.log('wrapper');
            }, false);
            content.addEventListener('click', function() {
                console.log('content');
            }, false);
            box.addEventListener('click', function() {
                console.log('box');
            }, false);
        </script>
    </body>
    
    事件冒泡
    2. 事件捕获
    • 与冒泡顺序相反,结构上嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素。(自顶向下)
    • 注意:IE没有捕获事件
    <style>
      .wrapper{
        width: 300px;
        height: 300px;
        background-color: red;
      }
      .content {
        width: 200px;
        height: 200px;
        background-color: green;
      }
      .box {
        width: 100px;
        height: 100px;
        background-color: yellow;
      }
    </style>
    ... ...
    <body>
        <div class="wrapper">
            <div class="content">
                <div class="box"></div>
            </div>
        </div>
        
        <script>
            var wrapper = document.getElementsByClassName('wrapper')[0];
            var content = document.getElementsByClassName('content')[0];
            var box = document.getElementsByClassName('box')[0];
    
            wrapper.addEventListener('click', function() {
                console.log('wrapper');
            }, true);
            content.addEventListener('click', function() {
                console.log('content');
            }, true);
            box.addEventListener('click', function() {
                console.log('box');
            }, true);
        </script>
    </body>
    
    事件捕获
    3. 阻止事件冒泡
    • event.stopPropagation(), 该方法不支持IE9以下版本。
    <style>
      .wrapper {
        width: 300px;
        height: 300px;
        background-color: red;
      }
    </style>
    ... ...
    <body>
        <div class="wrapper"></div>
        <script>
            var div = document.getElementsByTagName('div')[0];
            document.onclick = function() {
                console.log('点我干啥');
            }
            div.onclick = function(e) {
                e.stopPropagation();
                this.style.background = "orange";
            }
        </script>
    </body>
    
    • event.cancelBubble = true, 该方法兼容IE。
    <body>
        <div class="wrapper"></div>
        <script>
            var div = document.getElementsByTagName('div')[0];
            document.onclick = function() {
                console.log('点我干啥');
            }
            div.onclick = function(e) {
                e.cancelBubble = true;
                this.style.background = "orange";
            }
        </script>
    </body>
    
    • 封装取消冒泡函数
    function stopBubble(event) {
      if (event.stopPropagation) {
        event.stopPropagation();
      } else {
        event.cancelBubble = true;
      }
    }
    
    4. 阻止默认事件

    默认事件 —— 表单提交, a标签跳转, 右键菜单等。
    科普科普,右键出菜单事件 document.oncontextmenu = function() {}

    • return false;,以对象属性的方式注册的事件才生效
    document.oncontextmenu = function() {
      console.log('a');
      return false;
    }
    
    • event.preventDefault();,该方法IE9以下不支持
    document.oncontextmenu = function(e) {
      console.log('a');
      e.preventDefault();
    }
    
    • event.returnValue = false;,该方法兼容IE
    document.oncontextmenu = function(e) {
      console.log('a');
      e.returnValue = false;
    }
    
    • 封装阻止默认事件函数
    // 使用
    document.oncontextmenu = function(e) {
      console.log('sun');
      cancelHandler(e);
    };
    // 封装的函数
    function cancelHandler(e) {
      if (e.preventDefault) {
        e.preventDefault();
      } else {
        e.returnValue = false;
      }
    }
    
    5. 事件对象
    • event || window.event 兼容IE的写法
    <script>
      var div = document.getElementsByTagName('div')[0];
      div.onclick = function(e) {
        var event = e || window.event;
        console.log(event);
      }
    </script>
    
    • 事件源对象:
      • event.target 火狐只有这个
      • event.srcElement IE只有这个
      • chrome 这俩都有
      • 封装一个兼容函数,获取事件源对象
      <body>
          <div class="wrapper">
              <div class="box"></div>
          </div>
          <script>
              var wrapper = document.getElementsByClassName('wrapper')[0];
              var box = document.getElementsByClassName('box')[0];
              wrapper.onclick = function(e) {
                  var event = e || window.event;
                  var target = event.target || event.srcElement;
                  console.log(target);
              }
          </script>
      </body>
      
    6. 事件委托
    • 利用事件冒泡和事件源对象进行处理
    • 性能好,不需要循环所有的元素一个个去绑定事件
    • 灵活,当有新的子元素时,不需要重新绑定事件
    <body>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
            <li>7</li>
            <li>8</li>
            <li>9</li>
            <li>10</li>
        </ul>
        
        <script>
            var ul = document.getElementsByTagName('ul')[0];
            ul.onclick = function(e) {
                var event = e || window.event;
                var target = event.target || event.srcElement;
                console.log(target.innerText);
            }
        </script>
    </body>
    

    12. 事件分类

    1. 鼠标事件
    • click, mousedown, mousemove, mouseup, contextmenu, mouseover, mouseout, mouseenter, mouseleave

    • 用 button 来区分鼠标的按键, 0/1/2

    document.onmousedown = function(e) {
      console.log(e);
      if (e.button == 2) {
        console.log('right');
      } else if (e.button == 0) {
        console.log('left');
      }
    }
    
    左键点击,button为0
    右键点击,button为2
    • click 事件只能监听左键,只能通过 mousedownmouseup 来判断鼠标键。 右键无法触发click事件。

    • 解决 mousedownclick 的冲突
      用时间差来进行判断。

    var firstTime = 0;
    var lastTime = 0;
    var key = false;
    
    document.onmousedown = function() {
      firstTime = new Date().getTime();
    }
    document.onmouseup = function() {
      lastTime = new Date().getTime();
      if (lastTime - firstTime < 300) {
        key = true;
      }
    }
    document.onclick = function() {
      if (key) {
        console.log('click');
        key = false;
      } 
    }
    
    2. 键盘事件
    • keydown, keyup, keypress
      触发顺序:keydown > keypress > keyup

    • keydown 和 keypress 的区别
      keydown 可以响应任意键盘按键, keypress 只响应字符类键盘按键。
      keypress 返回 ASCII 码,可以转换成相应字符。

    <script>
      document.onkeypress = function (e) {
        console.log(String.fromCharCode(e.charCode));
      }
    </script>
    
    3. 移动端事件
    • touchstart
    • touchmove
    • touchend
    4. 事件分类
    • 文本类事件
      input, change, focus, blur
    <input type="text">
    ... ...
    // 但凡文本有变化,就会触发input事件
    var input = document.getElementsByTagName('input')[0];
    input.oninput = function (e) {
      console.log(this.value);
    }
    
    // 聚焦,改变内容,失去焦点,触发change事件
    input.onchange= function (e) {
      console.log(this.value);
    }
    
    • 窗体操作类 (window上的事件)
      scroll 当滚动条一滚动,这个事件就会被触发;
      load 这个方法是最慢的,这个页面所有要加载的要初始化的东西全部完成后,这个方法才会触发。
    window.onscroll = function () {
      console.log(window.pageXOffset + ' ' + window.pageYOffset);
    }
    
    window.onload = function () {
      // 没事情别用这个瓜皮方法
    } 
    

    小知识:
    浏览器有个时间线:
    (html和css是并行解析的)
    html在解析的时候会形成一个 domTree;所有的dom解析完毕,不是下载完。
    css在解析的时候会形成一个 cssTree;
    这俩会形成一个 renderTree;


    练习
    1. 遍历元素节点树 (在原型链上编程)
    2. 封装函数,返回元素e的第n层祖先元素节点
    function retParent(elem, n) {
     while(elem && n) {
        elem = elem.parentElement;
        n--;
      }
      return elem;
    }
    
    1. 封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己。
    • 不考虑兼容性
    function retSibling(e, n) {
      while(e && n) {
        if(n > 0) {
          e = e.nextElementSibling;
          n--;
        } else {
          e = e.previousElementSibling;
          n++;
        }
        return e;
      }
    }
    
    • 考虑兼容性
    function retSibling(e, n) {
      while(e && n) {
        if(n > 0) {
          if(e.nextElementSibling) {
            e = e.nextElementSibling;
          } else {
            for(e=e.nextSibling; e && e.nodeType  != 1; e = e.nextSibling);
          }
          n--;
        } else {
          if(e.previousElementSibling) {
            e = e.previousElementSibling;
          } else {
            for(e = e.previousSibling; e && e.nodeType != 1; e = e.previousSibling);
          }
          n++;
        }
        return e;
      }
    }
    
    1. 编辑函数,封装myChildren功能,解决以前部分浏览器的兼容性问题。(找该元素的子元素节点)
    Element.prototype.myChildren = function() {
      var child = this.childNodes;
      var len = child.length;
      var arr = [];
      for(var i=0; i<len; i++) {
        if(child[i].nodeType ==1) {
          arr.push(child[i]);
        }
      }
      return arr;
    }
    
    1. 自己封装hasChildren()方法,不可用children属性。
    Element.prototype.myChildren = function() {
      var child = this.childNodes;
      var len = child.length;
      for(var i=0; i<len; i++) {
        if(child[i].nodeType ==1) {
          return true;
        }
      }
      return false;
    }
    
    1. 请编写一段 JavaScript脚本生成下面这段DOM结构。要求,使用标准的DOM方法或属性。
      <div class="example">
      <p class="content"></p>
      </div>
    <body>
        <script>
            var div = document.createElement('div');
            var p = document.createElement('p');
            div.setAttribute('class', 'example');
            p.setAttribute('class', 'content');
            p.innerHTML = '这里是仙女萝';
            div.appendChild(p);
            document.body.appendChild(div);
        </script>
    </body>
    
    效果
    1. 封装函数 insertAfter();功能类似 insertBefore();
    <body>
        <div>
            <i></i>
            <b></b>
            <span></span>
        </div>
    
        <script>
            Element.prototype.insertAfter = function(targetNode, afterNode) {
                var beforeNode = afterNode.nextElementSibling;
                if(beforeNode == null) {
                    this.appendChild(targetNode);
                } else {
                    this.insertBefore(targetNode, beforeNode);
                }            
            }
            var div = document.getElementsByTagName('div')[0];
            var i = document.getElementsByTagName('i')[0];
            var b = document.getElementsByTagName('b')[0];
            var span = document.getElementsByTagName('span')[0];
            var p = document.createElement('p');
        </script>
    </body>
    
    效果
    1. 将目标节点内部的节点顺序逆序
      example: <div><a></a><em></em></div>
      <div><em></em><a></a></div>
    <body>
        <div>
            <i></i>
            <b></b>
            <span></span>
        </div>
    
        <script>
            var div = document.getElementsByTagName('div')[0];
            Element.prototype.reverseChild = function() {
                var arr = this.children;
                for(let i = arr.length-1; i > 0; i --) {
                    // console.log(arr[i-1]);
                    this.appendChild(arr[i-1]);
                }
            }
        </script>
    </body>
    
    效果
    1. 计时器,到三分钟停止
    <body>
        minutes: <input type="text" value="0">
        seconds: <input type="text" value="0">
    
        <script>
            var minutesNode = document.getElementsByTagName('input')[0];
            var secondsNode = document.getElementsByTagName('input')[1];
            var minutes = 0, 
                seconds = 0;
            var timer = setInterval(function() {
                seconds ++;
                if(seconds === 60) {
                    seconds = 0;
                    minutes ++;
                }
                secondsNode.value = seconds;
                minutesNode.value = minutes;
                if(minutes == 3) {
                    clearInterval(timer);
                }
            }, 1000);
        </script>
    <body>
    

    10 . 让小方块移动

    <body>
        <div style="width: 100px; height: 100px; background-color:pink;
        position: absolute; left: 0; top: 0;"></div>
        <script>
            var div = document.getElementsByTagName('div')[0];
            
            function getStyle(elem, prop) {
                if(window.getComputedStyle) {
                    return window.getComputedStyle(elem, null) [prop];
                } else {
                    return elem.currentStyle[prop];
                }
            }
    
            var timer = setInterval(function() {
                div.style.left = parseInt(getStyle(div, 'left')) + 10 + 'px';
                if(parseInt(div.style.left) > 500) {
                    clearInterval(timer);
                }
            }, 100)
        </script>
    </body>
    
    1. 使用原生js,addEventListener,给每个li元素绑定一个click事件,输出他们的顺序。
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
    <script>
      var liCol = document.getElementsByTagName('li');
      var len = liCol.length;
      for(var i=0; i<len; i++) {
        (function(i) {
          liCol[i].addEventListener('click', function() {
            console.log(i);
          }, false)
        }(i))
      }
    </script>
    
    1. 取消a标签默认事件
    var a = document.getElementsByTagName('a')[0];
    a.onclick = function() {
      return false;
    }
    
    <a href="javascript:void(0)">demo</a>
    
    1. 小方块拖拽
    <body>
        <div style="width: 100px; height: 100px; background-color: orange; 
        position:absolute; left: 0; top: 0;"></div>
        
        <script>
            var div = document.getElementsByTagName('div')[0];
            var disX,
                disY;
    
            div.onmousedown = function(e) {
                disX = e.pageX - parseInt(div.style.left);
                disY = e.pageY - parseInt(div.style.top);
                document.onmousemove = function (e) {
                    var event = e || window.event;
                    console.log(e.pageX + " " + e.pageY);
                    div.style.left = event.pageX - disX + 'px';
                    div.style.top = event.pageY -disY + 'px';
                }   
                document.onmouseup = function() {
                    document.onmousemove = null;
                }
            }
        </script>
    </body>
    
    <script>
      // 阻止默认事件
      function cancelHandler(e) {
        if (e.preventDefault) {
          e.preventDefault();
        } else {
          e.returnValue = false;
        }
      }
    
      // 阻止冒泡
      function stopBubble(event) {
        if (event.stopPropagation) {
          event.stopPropagation();
        } else {
          event.cancelBubble = true;
        }
      }
    
      // 绑定事件
      function addEvent(elem, type, handle) {
        if (elem.addEventListener) {
          elem.addEventListener(type, handle, false);
        } else if(elem.attachEvent) {
          elem.attachEvent('on' + type, function () {
            handle.call(elem);
          })
        } else {
          elem['on' + type] = handle;
        }
      }
    
      // 鼠标移动时
     function mouseMove(e) {
        var event = e || window.event;
        elem.style.left = event.clientX - disX + 'px';
        elem.style.top = event.clientY - disY + 'px';
      }
      
      // 鼠标松开时
      function mouseUp(e) {
        var event = e || window.event;
        removeEvent(document, 'mousemove', mouseMove);
        removeEvent(document, 'mouseup', mouseUp);
      }
    
      // 鼠标拖拽
      function drag(elem) {
        var disX,
            disY;
        addEvent(elem, 'mousedown', function(e) {
            var event = e || window.event;
            disX = event.clientX - parseInt(getStyle(elem, 'left'));
            disY = event.clientY - parseInt(getStyle(elem, 'top'));
            addEvent(document, 'mousemove', mouseMove);
            addEvent(document, 'mouseup', mouseUp);
            stopBubble(event);
            cancelHandler(event);
        });
      } 
    </script>
    
    1. 输入框功能完善
     <input typt="text" value="请输入用户名" 
        style="color: #999"
        onfocus="if(this.value == '请输入用户名'){this.value=''; this.style.color='#424242'}"
        onblur="if(this.value == ''){this.value='请输入用户名'; this.style.color='#999'}">
    
    1. 二阶菜单栏

    相关文章

      网友评论

          本文标题:DOM (Document Object Model)

          本文链接:https://www.haomeiwen.com/subject/yyxpgqtx.html