• 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查看面试题二
    

    后续会继续补充

  • 相关阅读:
    android.mk 文件中变量
    【转】An Intuitive Guide To Exponential Functions & e
    卷积相关
    opencv 调试时不显示图像
    Luhn 算法-- 信用卡号码的校验
    perl 脚本实现简单搜索修改文件并生成diff文件
    【转】gcc: multiple definition of [转]
    【转】undefined reference问题总结
    【转】pthread_cond_t怎么使用
    linux编程使用记录
  • 原文地址:https://www.cnblogs.com/pengdt/p/12037542.html
Copyright © 2020-2023  润新知