BOM和DOM

179 阅读8分钟

BOM

全称 browser object model - 浏览器对象模型

浏览器对象模型:使用对象组成的结构对浏览器进行操作

navigator

存储浏览器信息

appName ==> application name ==> 应用程序名称 ==> 记录的是浏览器名称

history

操作浏览器的历史记录

back():后退一个页面

btn.onclick = function() {
    // 回退
    history.back()
}

forward():前进一个页面

btn.onclick = function() {
    // 前进
    history.forward()
}

go():前进或后退多个页面

btn.onclick = function() {
    // history.back()

    // go方法可以前进或后退多个页面
    // 数字是几,就前进或后退几个页面
    // 前进用正数   后退用负数
    history.go(-2)
}

location

操作地址栏url

属性href:获取/设置完整的url

获取完整的地址
location.href
console.log(location.href);

设置完整的地址
location.href = 新地址
location.href = 'http://baidu.com'

属性search:获取地址url中的参数/数据部分

设置地址栏参数
location.search = ?键=值&键=值
location.search = '?a=1&b=2'

获取地址栏参数
location.search
console.log(location.search);

属性hash:获取地址栏中的锚点

设置锚点
location.hash = #单词
location.hash = '#abc'

获取锚点
location.hash
console.log(location.hash);

window的属性

获取浏览器窗口大小

innerWidth属性

console.log(innerWidth);

innerHeight属性

console.log(innerHeight);

都会包含滚动条的宽和高

window方法

弹出层

window.alert(123)
window.confirm(123)
window.prompt('143qwasdf')

全局定义的函数,都属于window的方法

全局定义的变量,都属于window的属性

定时器

setInterval(函数, 毫秒数):让函数每隔毫秒数,就执行一次,会不停的执行下去

利用返回值,可以停止定时器 - clearInterval(返回值)

setTimeout(函数, 毫秒数):让函数延迟毫秒数执行一次,就这一次

返回这个定时器在页面中是第几个,利用返回值,可以停止定时器 - clearTimeout(返回值)

var a = setTimeout(function() {
    console.log(1111);
}, 3000)
console.log(a);

btn.onclick = function() {
    clearTimeout(a)
}

window.open(url):打开新的标签页

window.open('http://baidu.com') // 新打开一个标签页

window.close():关闭窗口

window.scrollTo(x, y):设置卷去的距离

控制滚动条位置
window.scrollTo(200, 500)

window事件

window.onload:当所有资源加载完成后,触发执行

window.onresize:当浏览器窗口大小发生改变,触发执行

window.onresize = function() {
    console.log(innerWidth, innerHeight);
}

window.onscroll:当浏览器卷去的距离发生改变,触发执行

window.onscroll = function() {
    console.log(666);
}

DOM

document object model 文档对象模型,由对象组成的结构操作html文档

获取标签

document.querySelector(css选择器)

获取满足css选择器的第一个标签,返回一个标签

var li = document.querySelector('ul li')
console.log(li);

document.querySelectorAll(css选择器)

获取所有满足css选择器的标签,返回一个集合

var lis = document.querySelectorAll('ul li')
console.log(lis);

document.getElementById('id名') - 只能获取到一个标签

console.log( document.getElementById('var') );

document.getElementsByClassName('类名') - 只要标签有这个类名,就都可以获取到

console.log( document.getElementsByClassName('uu') );
console.log( document.getElementsByClassName('uu')[0] );

document.getElementsByTagName('标签名')

console.log( document.getElementsByTagName('ul') );
console.log( document.getElementsByTagName('ul')[0] );

document.getElementsByName('name属性的值')

console.log( document.getElementsByName('myul') );

html基本结构标签

html标签

console.log( document.documentElement );
body标签
console.log( document.body );

head标签

console.log( document.head );

网页标题

console.log( document.title ); // 获取标签

document.title = '这是我的网页' // 设置网页标题

获取标签名

标签.tagName

console.log( oDiv.tagName );

操作内容

文本内容

标签.innerText:给标签设置内容

oDiv.innerText = '哈哈'

获取标签内容:标签.innerText

console.log(oDiv.innerText);

带标签的内容

oDiv.innerHTML = '<b>哈哈</b>'  //设置
console.log(oDiv.innerHTML);   //获取

表单标签的内容

标签.value = 值

ipt.value = '张三'

标签.value

console.log(ipt.value);

操作属性

设置属性 标签.setAttribute(属性名, 属性的值)

box.setAttribute('name', 'dd')
box.setAttribute('name', 'mydd')

获取属性的值 标签.getAttribute(属性名)

console.log( box.getAttribute('name') );
console.log( box.getAttribute('class') );

删除属性 标签.removeAttribute(属性名)

box.removeAttribute('name')

样式操作

获取标签样式:getComputedStyle(标签)--- 返回所有css键值对组成的对象

console.log( getComputedStyle(box) );

设置样式

标签.style.css键 = 值 ---- 设置到行内

box.style['margin-left'] = '100px'
如果css键中包含连字符,有歧义,就必须使用中括号语法了
将带有连字符的css键使用小驼峰写法
box.style.marginLeft = '100px'   
box.style.width = '400px'
box.style.height = '400px'

类名操作

直接操作class属性值

标签.className = 值

标签.className

box添加类名mybox
console.log(box.className);
box.className = 'mybox' // 重新设置class属性的值

标签.classList

add():添加一个类名

box.classList.add('mybox')

remove():删除一个类名

box.classList.remove('mybox')

contains():判断是否有这个类名

console.log( box.classList.contains('mybox') );

toggle():让类名在删除和添加之间切换

btn.onclick = function() {
    // 让某个类名在删除和添加之间切换
    box.classList.toggle('mybox')
}

获取卷去的距离

有文档声明

document.documentElement.scrollTop

没有文档声明

document.body.scrollTop

万能写法

var t = document.documentElement.scrollTop || document.body.scrollTop
    // console.log(t);
    
document.documentElement.scrollTop = document.body.scrollTop = 数字

短路运算

使用逻辑运算符 && 和 || 给变量进行赋值

var 变量 = 值1 && 值2

如果左边为true,就会将右边的值赋值给变量

如果左边为false,就会将左边的值赋值给变量

var b = 2 && 3;
console.log(b); // 3

var c = 5 && false
console.log(c); // false

var 变量 = 值1 || 值

如果左边为true,就会将左边的值赋值给变量

如果左边为false,就会将右边的值赋值给变量

var a = 1 || 2;
console.log(a); // 1

var a = 0 || 2
console.log(a); // 2

国庆倒计时案例

<div class="box">
    距离国庆还有: <span>0</span> 天 <span>0</span> 小时 <span>0</span> 分钟 <span>0</span> 秒
</div>

// 获取所有标签
var spans = document.querySelectorAll('span')
daojishi()
function daojishi() {
    // 获取国庆的时间日期对象中的时间戳
    var guoqing = new Date('2023-10-1 0:0:0')
    guoqing = guoqing.getTime()
    // 获取当前的时间戳
    var now = new Date()
    now = now.getTime()
    // 求毫秒差
    var diff = guoqing - now
    // 根据毫秒差换算天、小时、分钟、秒
    var day = parseInt(diff / 1000 / 60 / 60 / 24)
    var hour = parseInt(diff / 1000 / 60 / 60) % 24
    var minute = parseInt(diff / 1000 / 60) % 60
    var second = parseInt(diff / 1000)  % 60
    // 将天、小时、分钟、秒放在对应的span标签中
    spans[0].innerText = day
    spans[1].innerText = hour
    spans[2].innerText = minute
    spans[3].innerText = second
}
// 设置定时器
setInterval(daojishi, 1000)

网页换皮肤案例

<div class="box">
    <img src="./images/1.jpg" alt="">
    <img src="./images/2.jpg" alt="">
    <img src="./images/3.jpg" alt="">
</div>
<button>按钮</button>

<script>
var btn = document.querySelector('button')
var box = document.querySelector('.box')
var imgs = document.querySelectorAll('.box img')
btn.onclick = function() {
    // 将box的height设置为200px
    // box.style.height = '200px'
    var height = getComputedStyle(box).height
    if (height === '200px') {
        box.style.height = 0
    } else {
        box.style.height = '200px'
    }
}
// 给每个图片都添加点击事件
for (var a = 0; a < imgs.length; a++) {
    imgs[a].onclick = function() {
        // 获取当前图片的路径名
        var imgPath = this.getAttribute('src')
        // 将当前图片设置为body的背景
        // document.body.style.background = 'url(' + imgPath + ') no-repeat 0 0/100% 100%'
        document.body.style.backgroundImage = 'url(' + imgPath + ')'
        document.body.style.backgroundRepeat = 'no-repeat'
        document.body.style.backgroundSize = '100% 100%'

        box.style.height = '0'
    }
}
</script>

节点操作

节点:html文档的每一个组成部分,最小的组成单位

创建标签

document.createElement(标签名)

var div = document.createElement('div')
div.innerText = '盒子'
div.className = 'box'
div.style.width = '200px'
div.setAttribute('name', 'mybox')
console.log(div);

插入标签

父.appendChild(子)

父.insertBefore(新, 旧)

document.body.appendChild(div)
document.body.insertBefore(div, ul)

获取标签

获取子标签 - 父.children

console.log( ul.children );

获取某个标签的上一个兄弟标签 - 标签.previousElementSibling

var fourth = document.querySelector('.fourth')
console.log(fourth);
console.log( fourth.previousElementSibling );

下一个兄弟标签 - 标签.nextElementSibling

console.log ( fourth.nextElementSibling );

获取父标签 - 子.parentElement

console.log( fourth.parentElement );

第一个子 - 父.firstElementChild

console.log( ul.firstElementChild );

最后一个子 - 父.lastElementChild

console.log( ul.lastElementChild );

替换标签

父.replaceChild(新, 旧的子标签)

var b = document.createElement('b')
b.innerText = '加粗'
// 使用b标签替换掉fourth
ul.replaceChild(b, fourth)

删除标签

父.removeChild(子)

ul.removeChild(fourth)

复制标签

标签.cloneNode()

// 如果要将标签中的内容和子标签都复制出来,就给方法添加参数true
console.log( fourth.cloneNode(true) );

标签的尺寸大小

带边框

标签.offsetWidth

标签.offsetHeight

var w2 = box.offsetWidth
var h2 = box.offsetHeight
console.log(w2, h2);

不带边框

标签.clientWidth

标签.clientHeight

var box = document.querySelector('.box')
var w1 = box.clientWidth
var h1 = box.clientHeight
console.log(w1, h1);

标签位置

参照设置过定位的直系祖宗标签

标签.offsetLeft

标签.offsetTop

var l1 = big.offsetLeft
var t1 = big.offsetTop
console.log(l1, t1);

var small = document.querySelector('.small')
var l2 = small.offsetLeft
var t2 = small.offsetTop
console.log(l2, t2);

边框的厚度

标签.clientLeft

标签.clientTop

var box = document.querySelector('.box')
var l = box.clientLeft
console.log(l);

var t = box.clientTop
console.log(t);

窗口大小不包含滚动条

document.documentElement.clientWidth

document.documentElement.clientHeight

var w1 = innerWidth
var h1 = innerHeight

var w2 = document.documentElement.clientWidth
var h2 = document.documentElement.clientHeight
console.log(w1, h1);
console.log(w2, h2);

回流和重绘

浏览器的渲染过程

浏览器会先解析html成DOM树,同时解析css成样式规则;

将DOM树和css规则合成渲染树

布局:计算标签关系、位置、大小

喷绘:给标签涂颜色

让浏览器显示

回流和重绘

回流:操作需要重新布局

如果动态操作了标签的节点、位置、大小,浏览器需要重新计算标签的大小和位置,也就造成了回流

setStyle(div, {
        width: 0,
        height: '8px',
        border: '1px solid #000',
        position: 'absolute',
        left: '100px',
        top: 0,

        // 设置旋转的原点
        transformOrigin: '1px 100px',
        transform: 'rotate('+0*6+'deg)'
    })
}
function setStyle(ele, obj) {
    for (var key in obj) {
        ele.style[key] = obj[key]
    }
    // 循环一次,就设置一次,回流很多次
    // 优化:将所有的样式拼接成一个style属性的值 === > 字符串;通过设置style属性,造成1次回流即可
    str=width:100px;height:100px;border:1px solid #000;
   // 设置标签的style属性,值为str
    var arr = []
    for (var key in obj) {
        arr.push( key + ':' + obj[key] + ';' )
    }
    // console.log(arr);
    var str = arr.join('')
    // console.log(str);
    // ele.setAttribute('style', str)
    // js还提供了设置样式的属性 - cssText
    ele.style.cssText = str
}

重绘:操作需要重新喷绘

如果动态操作了标签的颜色等属性,就需要重新喷绘,也就造成了重绘

<style>
        .box{
            width: 100px;
            height: 100px;
            background-color: #f00;
        }
</style>

<body>
<div class="box"></div>
</body>

<script>
document.querySelector('.box').style.backgroundColor = '#0f0'
</script>    

都影响了渲染性能

优化

合并样式修改

cssText style属性 类名

优化批量的DOM操作

隐藏,操作,显示

ul.style.display = 'none'
for (var a = 0; a < 10; a++) {
    var li = document.createElement('li')
    li.innerText = a + 1
    ul.appendChild(li)
}
ul.style.display = 'block'

复制,操作,替换

var newUL = ul.cloneNode(true)
// console.log(newUL);
for (var a = 0; a < 10; a++) {
    var li = document.createElement('li')
    li.innerText = a + 1
    newUL.appendChild(li)
}
console.log(newUL);
ul.parentElement.replaceChild(newUL, ul)

文档碎片(虚拟标签)

var fg = document.createDocumentFragment() // 创建文档碎片
for (var a = 0; a < 10; a++) {
    var li = document.createElement('li')
    li.innerText = a + 1
    fg.appendChild(li)
}
console.log(fg);
// 将文档碎片放在ulul.appendChild(fg)

优化定时器中的DOM操作

将定时器中获取标签的操作,放在定时器外面

 // 获取原本的宽度
    var w = small.offsetWidth
    timer = setInterval(function() {
    ...
    },1000)

手动封装两个数字的数学运算函数

function math(a, b, operator) {
    // 将两个数字转成字符串
    a = '' + a
    b = '' + b
    // 判断这两个数是否整数
    // 判断字符串是否包含 .
    if (!a.includes('.') && !b.includes('.')) {
        // 直接进行整数运算就好
        var result
        switch (operator) {
            case '+':
                result = a + b
            break
            case '-':
                result = a - b
            break
            case '*':
                result = a * b
            break
            case '/':
                result = a / b
            break
        }
        return result
    }
    // 不是整数
    // 找a和b中.所在下标
    var aDotIndex = a.indexOf('.')
    var bDotIndex = b.indexOf('.')
    // 根据下标截取小数点后所有字符串
    var aSuffix = a.slice(aDotIndex)
    var bSuffix = b.slice(bDotIndex)
    // 求最长的位数
    var max = Math.max(aSuffix.length, bSuffix.length)
    // 计算要乘以的数字
    var num = Math.pow(10, max)
    // 将两个数字都乘以num
    a = a * num
    b = b * num
    var result
    switch (operator) {
        case '+':
            result = (a + b) / num
        break
        case '-':
            result = (a - b) / num
        break
        case '*':
            result = (a * b) / Math.pow(10, max * 2)
        break
        case '/':
            result = a / b
        break
    }
    return result
}

console.log(math(1, 2, '/'))