zoukankan      html  css  js  c++  java
  • dom与jq基础使用

    js有两种运行环境,一个是浏览器,一个是服务器(NodeJS)

    js的本质是es,因为运行环境的不同,为了操作环境内的api做了升级

    在浏览器上js分为es + dom + bom
    在服务器上js又有另外的功能,这个在NodeJS里讲

    dom和bom就是环境自带的东西

    在css的dom树里讲过,浏览器把标签解析成一个巨大的对象renderTree,然后js出现了能操作renderTree的功能,每一个标签都是一个独立的可以被js单独操作的对象

    注意:在html的标签,元素,在js叫节点node 都是同个意思,他们是同个东西

    就拿把一个按钮的字变成红色来说
    原生的js都需要这样几个步骤
    指定获取这个按钮,通过什么呢,跟css一样

    //这个就是dom对象
    document.querySelector("#btn")
    //怎么改颜色呢?控制这个dom对象的style属性,这个属性也是一个对象,这个对象存的就是有关样式的资料,有些可以改变,有些是只读属性
    document.querySelector("#btn").style.color = "red";
    

    那我怎么知道这个对象里有一个叫style的属性对象呢
    这就是写多了就知道了,那他还有什么属性对象呢?这就是dom元素的调试
    按下F12,从左数第4个console就是调试窗口,这个窗口是前端工作者的家,是最常用的地方

    image.png

    自己写一个有div的页面打开后,打开console,依次输入下面三句js

    // 这个只能拿到页面上的一样的标签
    document.querySelector("div")
    // 把上面的标签转成详细dom对象
    console.dir(document.querySelector("div"))
    // 上面的简化版,偷懒就这么写
    [document.querySelector("div")]
    

    然后打开返回的数据的箭头,你会看到一个特别长的对象格式的数据,里面就有上面说的style属性对象,这样的dom对象一个页面有无数个

    我们去重复的写document.querySelector("xxx")是非常的恶心的,而且有个悲伤的故事不得不讲,就是不同的浏览器的dom是不一样的,因为有个叫内核的东西,内核决定了解析效果,如果接触过IE等非chorme浏览器,你就会知道什么叫内核,因为渲染的不同,属性也会不一样,比如在这个浏览器颜色是color,另一个可能叫myColor,当然这只是比喻,导致我本地运行没问题的代码,给不同的用户使用就没效果,然后被领导疯狂的怼,于是有个优秀的团队封装了一个叫jquery的插件,专门用来操作dom对象,并且做了简化和浏览器内核兼容,简称jq

    下面对比原生的js-dom和jq的使用
    有很多人的原生js一点都不懂,刚接触就用jq,这是非常不好的
    下面的内容不是告诉你jq有多好用,而是原生应该怎么实现jq的方法
    大佬对jq的源码解读

    元素获取

    // jq写法
    $("#id")
    $(".class")
    $("div")
    $("[name=xx]")
    $("[type=radio]")
    $("input[type=radio]:checked")
    $("select option:selected")
    $("[disabled]")
    // jq把获取一个和获取多个都封装进了$()里
    // 原生js就要根据自己的情况去选择
    // 使用 document.querySelector()
    // 还是 document.querySelectorAll()
    // 括号里的写法在原生一样适用
    

    通过name取form表单里的带有name的form表单标签

    // 比如有个name属性是myform的form标签里有个name属性是name叫nameInp的输入框
    document.myform.nameInp  //不放在form里是拿不到的
    

    选择多个的第N个

    // jq写法
    $("div").eq(2)
    // 原生js写法
    document.querySelectorAll("div")[2]
    

    jq转js,jq的核心就是把节点存进一个数组里

    $("#id")[0] 跟 document.querySelector("#id") 是一样的
    $(".class")[4] 跟 document.querySelectorAll(".class")[4] 是一样的
    // 一旦把jq转成js就不能再使用jq的方法
    

    节点的循环

    // jq写法
    $("div").each(function(index,node){ ... })
    // 原生js写法
    var divs = document.querySelectorAll("div")
    for(var i=0;i<divs.length;i++){  ...  }
    

    元素的样式和属性

    window的高度获取

    // jq写法
    $(window).height();
    // 原生js写法
    // 含 scrollbar
    window.document.documentElement.clientHeight;
    // 不含 scrollbar,与 jQuery 行为一致
    window.innerHeight;
    

    document的高度

    // jq写法
    $(document).height();
    // 原生js写法
    var body = document.body;
    var html = document.documentElement;
    var height = Math.max(
      body.offsetHeight,
      body.scrollHeight,
      html.clientHeight,
      html.offsetHeight,
      html.scrollHeight
    );
    

    某个元素的高度

    // jq写法
    $el.height();
    // 原生js写法
    function getHeight(el) {
      var styles = this.getComputedStyle(el);
      var height = el.offsetHeight;
      var borderTopWidth = parseFloat(styles.borderTopWidth);
      var borderBottomWidth = parseFloat(styles.borderBottomWidth);
      var paddingTop = parseFloat(styles.paddingTop);
      var paddingBottom = parseFloat(styles.paddingBottom);
      return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom;
    }
    

    获得匹配元素相对上一级div的坐标位置

    // jq写法
    $el.position();
    // 原生js写法
    el.offsetTop/offsetLeft
    

    获得匹配元素相对body的偏移

    // jq写法
    $el.offset();
    // 原生js写法
    function getOffset (el) {
      const box = el.getBoundingClientRect();
      return {
        top: box.top + window.pageYOffset - document.documentElement.clientTop,
        left: box.left + window.pageXOffset - document.documentElement.clientLeft
      }
    }
    

    获取元素滚动条垂直位置

    // jq写法
    $(window).scrollTop();
    // 原生js写法
    (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
    

    标签的属性
    checked,disabled,placeholder,自定义属性都可以用这个方法
    自定义属性在css篇里我说用来储存数据就是这个用法

    // jq获取属性
    $el.attr("src")
    // 原生js获取属性
    el.getAttribute("src")
    
    // jq设置属性
    $el.attr("data-xx","xx")
    // 原生js设置属性
    el.setAttribute("data-xx","xx")
    // 返回标签上所有data的自定义属性和值,格式是一个对象
    el.dataset
    
    // jq删除属性
    $el.removeAttr("data-xx")
    // 原生js删除属性
    el.removeAttribute("data-xx")
    
    // 原生js判断属性
    el.hasAttribute("data-xx")
    

    关于单选多选有个注意的点
    一般我们默认选中是用 checked="checked" 这是没错的
    如果选择是通过鼠标去点击选择或者取消选择,最后用 [xxx]:checked 去取值也是没错的
    但是
    判断有没有被选中不能通过判断节点是否有 checked 属性
    想通过js去选中或者取消选中不能通过添加和移除 checked 属性去实现
    复制下面的代码可以知道为什么

    <input type="checkbox" checked="checked" id="aa">
    
    document.querySelector("#aa").checked = false
    console.log($("#aa").attr("checked"))
    //虽然页面显示为选择,但是打印出来的属性还是选中状态,这就是BUG产生的隐患
    

    正确操作如下

    // 要判断有没有选中,true是有,false是没有
    document.querySelector("#aa").checked
    // 选中或者取消选中
    document.querySelector("#aa").checked = true/false
    

    form表单元素的值
    包括输入框,单选多选,下拉框,大输入框textarea

    // jq获取value
    $el.val()
    // js获取value
    el.value
    // jq修改value
    // 下拉框select传入option的value一样的值会修改选中选项
    $el.val(123456)
    // js获取value
    el.value = 123456
    

    关于下拉框的操作
    获取下拉框的值上面提到是value,但是value只是获取被选中的option标签里的value属性的值,那option的内容要怎么获取呢,怎么知道当前下拉框选中的是第几个option呢,怎么去修改当前选中的下拉框的值呢

    //获取当前选中的下拉框内容的序号
    var index = $('select').selectedIndex
    // 所有options的数组
    $('select').options 
    // option的内容
    $('select').options[index].text
    //选另一个
    $('select').options[index].setAttribute("selected","selected")
    

    获取内容html
    包括div,p,span等等,图片和表单元素没有这个值

    // jq获取html
    $el.html()
    // js获取html
    el.innerHTML
    // jq修改html,清空填""
    $el.html("<div>123456</div>")
    // js修改html,清空等于""
    el.innerHTML = "<div>123456</div>"
    

    操作class

    // jq添加,移除,有就移除没有就添加
    $el.addClass("xxx")
    $el.removeClass("xxx")
    $el.toggleClass("xxx")
    // 原生js添加,移除,有就移除没有就添加
    el.classList.add("xxx")
    el.classList.removeClass("xxx")
    el.classList.toggle("xxx")
    

    插入节点

    Append 插入到子节点的末尾

    // jq写法
    $el.append("<div id='container'>hello</div>");
    // 原生js写法
    el.insertAdjacentHTML('beforeend', '<div id="container">Hello World</div>');
    // 也可以先创建一个标签元素
    var newEl = document.createElement("div")
    el.appendChild(newEl);
    

    Append 插入到子节点的开头

    // jq写法
    $el.prepend("<div id='container'>hello</div>");
    // 原生js写法
    el.insertAdjacentHTML('afterbegin', '<div id="container">Hello World</div>');
    // 也可以先创建一个标签元素
    var newEl = document.createElement("div")
    el.insertBefore(newEl, el.firstChild);
    

    在选中元素前插入新节点

    // jq写法
    $el.insertBefore("<div id='container'>hello</div>");
    // 原生js写法
    el.insertAdjacentHTML('beforebegin ', '<div id="container">Hello World</div>');
    // 也可以
    const el = document.querySelector(selector);
    if (el.parentNode) {
      var newEl = document.createElement("div")
      el.parentNode.insertBefore(newEl, el);
    }
    

    在选中元素后插入新节点

    // jq写法
    $el.insertAfter("<div id='container'>hello</div>");
    // 原生js写法
    el.insertAdjacentHTML('afterend', '<div id="container">Hello World</div>');
    // 也可以
    const el = document.querySelector(selector);
    if (el.parentNode) {
      var newEl = document.createElement("div")
      el.parentNode.insertBefore(newEl, el.nextSibling);
    }
    

    替换元素

    // jq写法
    $el.replaceWith("<b>Paragraph. </b>");
    // 原生js写法
    var newEl = document.createElement("div");
    el.parentNode.replaceChild(newEl,el);
    

    移除一个元素

    // jq写法
    $el.remove()
    // 原生js写法
    el.parentNode.removeChild(el)
    //现在也是可以直接el.remove()的,只是不知道兼容性如何
    

    在html的第一篇里说标签除了属性就是方法,dom节点如何绑定一个方法
    让dom响应方法的方式有3种

    1. 在标签上去添加全局暴露的方法
    2. 通过选择器添加方法(这个又分为直接赋值和订阅发布)

    在标签上去添加全局暴露的方法
    关键词是全局,下面写的所有方法,必须是全局的,如果被私有的作用域保护,是找不到的,什么叫全局的方法,就是window.xx()可以执行的,或者在F12的调试框输入方法名找得到的,这个写法很不安全,因为可以被随意被调用,但是快,简单

    <div onclick="click()">点击时</div>
    <input type="text" onblur="blur()" oninput="input()" />光标选择时,输入时
    <input type="checkbox" onchange="change()" />选中或者取消选中
    <input type="file" onchange="change(this.files[0])" />传入文件时
    
    // 讲个特别的,阻止a标签的路径跳转
    // 但是为什么要用a标签,还要专门去阻止跳转,这就是傻逼行为
    // 知识点 void(0) 就是undefined的意思,很老很装逼的写法
    <a href="javascript:void(0)">跳转不了</a>
    

    通过选择器添加方法(这个又分为直接赋值和订阅发布)
    先说直接赋值,一个标签的同个方法只能赋值一次,新的会替换掉旧的

    //原生js写法,jq没有
    document.querySelector(el).onclick = function(){ ... }
    document.querySelector(el).onchange = function(){ ... }
    window.onscroll = function(){ ... }
    window.onload = function(){ ... }
    

    再说说订阅发布
    下面的写法不会因为新添加方法移除上一个方法,
    添加多少次方法就会执行多少次,会引起多次执行
    支持移除,但需要方法名作为标识

    //jq写法
    //这种写法不支持移除
    $el.click(function(){ ... })
    $el.change(function(){ ... })
    //这种写法可以移除
    $el.on("click",functionName)
    $el.remove("click",functionName)
    //这种写法不能移除,没有标识
    $el.on("click",function(){ ... })
    // 原生js写法
    document.querySelector(el).addEventListener("input",functionName)
    document.querySelector(el).removeEventListener("input",functionName)
    //这种写法不能移除,没有标识
    document.querySelector(el).addEventListener("input",function(){ ... })
    

    虽然上面有很多绑定方法的写法,但是通过id和class等标识去绑定的方式我不是很喜欢,我常用的写法有两种

    • 直接把方法写在标签里,方法写成全局的方法
    • 创建标签不用字符串,用creatElment,然后把创建好的dom元素直接addEventListener绑定方法

    注意
    上面的所有的事件绑定都必须保持原标签不改变的前提,一旦标签被父元素innerHTML=""清空,或者本身remove,之后就算再添加一个一样id一样class的标签,他也不是原来的标签了,因为他原本被赋予的方法被删除时已经消失了,需要重新添加方法

    dom方法的event对象
    任何关于dom元素的方法,他的作用域里都有一个隐藏的叫做event的对象
    event的对象有很多种,
    最原始的window的onload的Event,
    比如输入框失去光标的FocusEvent,
    比如键盘按下的KeyboardEvent,
    比如鼠标的MouseEvent,
    还有手机屏幕滑动的TouchEvent,
    等等

    $el.click(function(){ console.log(event) })
    window.onload = function(){ console.log(event) }
    document.querySelector(el).onclick = function(){ console.log(event) }
    window.ontouchmove = function(){  console.log(event)  }
    

    当给window添加点击事件时认真的去看那个event对象,会发现,event对象里有一个target属性,这个属性里的值就是被鼠标点到的节点,有了这个节点就可以进行节点的判断了,如果点击到的节点的className是aa执行aa方法,如果点到的节点是个img,就如何如何,event就是这样一个俯视节点的上帝的存在,这也是一种绑定事件的方法,叫做事件代理,除了点击事件外,其他事件做不出同样的效果

    我用到event的三个地方

    1. 一是写一个打气球的游戏;
    2. 还有解决苹果手机ios系统页面不会弹的BUG;
    3. 手机滑动手势计算

    TouchEvent
    • clientX:触摸目标在视口中的x坐标。
    • clientY:触摸目标在视口中的y坐标。
    • pageX:触摸目标在页面中的x坐标。
    • pageY:触摸目标在页面中的y坐标。
    • screenX:触摸目标在屏幕中的x坐标。
    • screenY:触摸目标在屏幕中的y坐标。

    事件冒泡和阻止冒泡
    在页面上我们会遇到这样的情况,divA有个点击事件A,还有个子元素divB,divB也有个点击事件B,这时鼠标点击B会执行什么事件呢?
    点击事件的执行是一种冒泡的模式,从最里面往外面执行,也就是先执行B,然后执行A,但是我们点击B却不想执行A,怎么办
    两种方法
    一种是把divB从divA里移出来
    另一种是阻止冒泡行为,就是在B方法里面写上

    function B(){  event.stopPropagation()  }
    

    event的其他事件

    // 阻止默认行为
    event.preventDefault()
    
    // 阻止剩余的事件处理函数执行并且防止事件冒泡到DOM树上
    // 这个方法不接受任何参数。
    // 例如注册了A、B两个 click 事件,在 A 的方法中阻止后,不会执行 B 的方法
    event.stopImmediatePropagation()
    

    dom方法的this对象
    每个function都有执行者,function的执行者就是function作用域内的this,这句概念使用在整个js领域,function的执行者就是一个对象,可以是构造对象,可以是dom对象,最常见的是全局对象(全局对象在浏览器端是window对象,在服务器端叫global对象)

    上面的几种事件绑定方式
    第一种标签绑定事件的默认this是window,需要让标签把自己传过来

    <div onclick="aa(this)"></div>
    

    最后一种event.target就是this
    其他几种就是直接

    function x(){ console.log(this) }
    

    》》其他
    这里的内容都很少用到,要么找插件,要么找插件

    画布/视频/音频

    // 画布api,画布比较常用会单独做一篇
    var canvas = document.getElementById("myCanvas");
    var cxt = canvas.getContext("2d");
    
    // 视频api
    var mp4 = document.getElementById("myVideo");
    mp4.onplay = function() {  alert("The video has started to play") }
    
    // 音频api
    var mp3 = document.getElementById("myAudio");
    mp3.onplay = function() {  alert("The Audio has started to play") }
    

    还有文件读取fileReader,文件详情DataView,IntersectionObserver是否在可见区域,文件容器FromData,iframe,富文本,拖拽上传,复制上传,地址,摄像头,录音,websocket,打印机

    // 文件读取会跟画布做在一期
    var fr = new FileReader()
    
    // iframe的高度等于内容的高度
    document.querySelector('#iframe').onload = function () {
        this.height = this.contentWindow.top.innerHeight + "px";
        if(this.contentDocument){
            // 不跨域的情况 
        }else{
            // 跨域的情况
            this.height = this.contentWindow.top.innerHeight + "px";
            this.style.marginLeft = "16px";
            document.body.style.overflowX = "hidden";
        }
    };
    
    // 富文本有相关3个api,会专门总结一期
    var selection = window.getSelection();
    var range = selection.getRangeAt;
    document.execCommand("Copy");
    
    // 拖拽上传
    $("#id")
      .on("dragover", function (event) {
        event.preventDefault();
      })
      .on("drop", function(event) {
        event.preventDefault();
        // 数据在event的dataTransfer对象里
        let file = event.originalEvent.dataTransfer.files[0];
        console.log(file)
        // 然后就可以使用FileReader进行操作
        // var fr = new FileReader();
        // fr.readAsDataURL(file);
        // 或者是添加到一个FormData
        // let formData = new FormData();
        // formData.append("file", file);
      })
    
    // 复制上传,查看富文本篇
    
    // IntersectionObserver查看面试题二
    

    后续会继续补充

  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/pengdt/p/12037542.html
Copyright © 2011-2022 走看看