MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript操作DOM属性的最佳实践

2022-05-184.0k 阅读

一、理解 DOM 属性

1.1 什么是 DOM 属性

在深入探讨如何操作 DOM 属性之前,我们需要先清楚地了解什么是 DOM 属性。DOM(文档对象模型)是 HTML 和 XML 文档的编程接口。它将文档呈现为带有元素、属性和文本的树结构。而 DOM 属性则是附加到 DOM 元素上的键值对,用于为元素提供额外的信息。

例如,在 HTML 中有一个 <img> 元素,它常见的属性有 src(指定图像的源文件路径)、alt(为图像提供替代文本,用于图像无法显示时)、widthheight(分别指定图像的宽度和高度)等。

1.2 DOM 属性与 HTML 属性的区别

虽然 DOM 属性和 HTML 属性看起来很相似,但它们之间存在一些重要的区别。HTML 属性是在 HTML 标记中定义的,用于描述元素的初始状态。而 DOM 属性是在 JavaScript 中操作 DOM 元素时所使用的对象属性。

例如,考虑一个 <input> 元素的 checked 属性。在 HTML 中,如果 <input type="checkbox" checked>,这里的 checked 是 HTML 属性,表示该复选框在页面加载时是选中状态。在 JavaScript 中,当我们获取这个 <input> 元素并访问其 checked 属性时,它是一个 DOM 属性,并且这个属性的值是一个布尔值(truefalse),表示当前复选框是否被选中。

再比如,value 属性在 <input> 元素上,HTML 属性值是初始值,而 DOM 属性值在用户输入后会实时更新。例如:

<input type="text" id="myInput" value="初始值">
<script>
    const input = document.getElementById('myInput');
    console.log(input.value); // 输出初始值
    // 用户在输入框中输入新内容后
    console.log(input.value); // 输出用户输入的新内容
</script>

二、获取 DOM 属性

2.1 使用 getAttribute 方法

JavaScript 提供了 getAttribute 方法来获取元素的属性值。该方法可以获取任何 HTML 属性的值,包括自定义属性。

示例:

<div id="myDiv" data-custom="自定义值">这是一个 div</div>
<script>
    const div = document.getElementById('myDiv');
    const customValue = div.getAttribute('data-custom');
    console.log(customValue); // 输出:自定义值
</script>

getAttribute 方法返回的是字符串类型的值,即使属性在 HTML 中看起来像布尔值,它也会返回字符串形式。例如:

<input type="checkbox" id="myCheckbox" checked>
<script>
    const checkbox = document.getElementById('myCheckbox');
    const checkedValue = checkbox.getAttribute('checked');
    console.log(checkedValue); // 输出:checked
</script>

2.2 直接访问 DOM 属性

对于大多数标准的 HTML 属性,我们可以直接通过 DOM 元素对象的属性来访问。例如,对于 <img> 元素的 src 属性,可以这样访问:

<img id="myImg" src="image.jpg">
<script>
    const img = document.getElementById('myImg');
    console.log(img.src); // 输出:image.jpg
</script>

直接访问 DOM 属性的优点是,它返回的是合适的数据类型,例如 checked 属性返回布尔值:

<input type="checkbox" id="myCheckbox" checked>
<script>
    const checkbox = document.getElementById('myCheckbox');
    console.log(checkbox.checked); // 输出:true
</script>

但是,这种方式不适用于自定义属性。如果要访问自定义属性,还是需要使用 getAttribute 方法。例如:

<div id="myDiv" data-custom="自定义值"></div>
<script>
    const div = document.getElementById('myDiv');
    console.log(div.data-custom); // 输出:undefined
    const customValue = div.getAttribute('data-custom');
    console.log(customValue); // 输出:自定义值
</script>

2.3 选择获取属性的方式

一般来说,如果要获取标准的 HTML 属性,并且希望得到合适的数据类型(如布尔值、数字等),直接访问 DOM 属性是更好的选择。而如果要获取自定义属性或者需要获取 HTML 属性原始的字符串值,getAttribute 方法则更为合适。

例如,对于 <a> 元素的 href 属性,直接访问 DOM 属性可以方便地获取完整的 URL 路径,并且如果需要对 URL 进行操作,它返回的是一个 URL 对象(在现代浏览器中),便于进一步处理:

<a id="myLink" href="https://example.com">点击我</a>
<script>
    const link = document.getElementById('myLink');
    const url = new URL(link.href);
    console.log(url.hostname); // 输出:example.com
</script>

而对于一些可能具有特殊格式的自定义属性,getAttribute 方法可以直接获取到原始的字符串值,便于后续根据自定义规则进行解析。

三、设置 DOM 属性

3.1 使用 setAttribute 方法

setAttribute 方法用于设置元素的属性值。它可以设置标准属性,也可以设置自定义属性。

示例:

<div id="myDiv"></div>
<script>
    const div = document.getElementById('myDiv');
    div.setAttribute('data-custom', '新的自定义值');
    div.setAttribute('class', 'new-class');
</script>

使用 setAttribute 方法设置布尔属性时,需要注意,对于布尔属性(如 checkeddisabled 等),不需要设置具体的值,只需要设置属性名即可表示开启该属性,移除该属性则表示关闭。例如:

<input type="checkbox" id="myCheckbox">
<script>
    const checkbox = document.getElementById('myCheckbox');
    checkbox.setAttribute('checked', '');
    // 等同于 checkbox.checked = true;
</script>

3.2 直接设置 DOM 属性

直接设置 DOM 属性也是一种常用的方式。对于标准属性,这种方式更加直观和便捷。

例如,设置 <img> 元素的 src 属性:

<img id="myImg">
<script>
    const img = document.getElementById('myImg');
    img.src = 'new-image.jpg';
</script>

对于布尔属性,直接设置 DOM 属性更加简单明了。例如,设置 <input> 元素的 disabled 属性:

<input type="text" id="myInput">
<script>
    const input = document.getElementById('myInput');
    input.disabled = true;
</script>

3.3 选择设置属性的方式

当设置标准属性时,直接设置 DOM 属性通常是首选,因为它更简洁,并且能正确处理数据类型。例如,设置 <input> 元素的 value 属性:

<input type="text" id="myInput">
<script>
    const input = document.getElementById('myInput');
    input.value = '新的值';
</script>

而当设置自定义属性或者需要设置特殊格式的属性值时,setAttribute 方法更为合适。例如,设置一个包含复杂 JSON 数据的自定义属性:

<div id="myDiv"></div>
<script>
    const div = document.getElementById('myDiv');
    const data = { key: 'value', list: [1, 2, 3] };
    div.setAttribute('data-complex', JSON.stringify(data));
</script>

四、移除 DOM 属性

4.1 使用 removeAttribute 方法

removeAttribute 方法用于移除元素的属性。它可以移除标准属性和自定义属性。

示例:

<div id="myDiv" data-custom="自定义值" class="old-class"></div>
<script>
    const div = document.getElementById('myDiv');
    div.removeAttribute('data-custom');
    div.removeAttribute('class');
</script>

对于布尔属性,移除属性就相当于关闭该属性。例如,移除 <input> 元素的 checked 属性:

<input type="checkbox" id="myCheckbox" checked>
<script>
    const checkbox = document.getElementById('myCheckbox');
    checkbox.removeAttribute('checked');
    // 等同于 checkbox.checked = false;
</script>

4.2 使用 delete 操作符(部分属性适用)

在 JavaScript 中,对于一些 DOM 属性,可以使用 delete 操作符来移除。但是,这种方式只适用于直接访问的 DOM 属性,不适用于通过 getAttribute 获取的属性。

例如,对于 <img> 元素的 src 属性:

<img id="myImg" src="image.jpg">
<script>
    const img = document.getElementById('myImg');
    delete img.src;
</script>

然而,并不是所有属性都能通过 delete 操作符成功移除。例如,对于 classList 属性,不能使用 delete 操作符移除:

<div id="myDiv" class="my-class"></div>
<script>
    const div = document.getElementById('myDiv');
    // 以下操作不会移除 class 属性
    delete div.classList;
</script>

4.3 选择移除属性的方式

对于大多数情况,removeAttribute 方法是移除属性的可靠选择,因为它适用于所有类型的属性,无论是标准属性还是自定义属性。

delete 操作符虽然在某些特定情况下可以使用,但由于其适用范围有限,并且可能会导致一些兼容性问题,所以在实际应用中应谨慎使用。例如,在一些旧版本的浏览器中,delete 操作符对某些 DOM 属性的行为可能不一致。

五、常见 DOM 属性操作场景及最佳实践

5.1 表单元素操作

5.1.1 处理表单输入值

在处理表单元素时,经常需要获取和设置输入值。对于文本输入框、密码输入框等 <input> 元素,直接访问 value DOM 属性是最佳方式。

例如,获取文本输入框的值并进行验证:

<input type="text" id="nameInput">
<button onclick="validateName()">提交</button>
<script>
    function validateName() {
        const input = document.getElementById('nameInput');
        const value = input.value;
        if (value.length < 3) {
            alert('名字长度至少为 3 个字符');
        } else {
            alert('名字验证通过');
        }
    }
</script>

设置输入框的值也同样简单,比如根据用户的选择填充某些字段:

<select id="colorSelect">
    <option value="red">红色</option>
    <option value="blue">蓝色</option>
    <option value="green">绿色</option>
</select>
<input type="text" id="colorInput">
<button onclick="fillColor()">填充颜色</button>
<script>
    function fillColor() {
        const select = document.getElementById('colorSelect');
        const input = document.getElementById('colorInput');
        input.value = select.value;
    }
</script>

5.1.2 处理复选框和单选框

对于复选框和单选框,直接访问 checked DOM 属性来获取和设置其选中状态。

例如,获取所有选中的复选框的值:

<input type="checkbox" value="apple" id="appleCheckbox">苹果
<input type="checkbox" value="banana" id="bananaCheckbox">香蕉
<input type="checkbox" value="cherry" id="cherryCheckbox">樱桃
<button onclick="getCheckedFruits()">获取选中水果</button>
<script>
    function getCheckedFruits() {
        const appleCheckbox = document.getElementById('appleCheckbox');
        const bananaCheckbox = document.getElementById('bananaCheckbox');
        const cherryCheckbox = document.getElementById('cherryCheckbox');
        const checkedFruits = [];
        if (appleCheckbox.checked) {
            checkedFruits.push(appleCheckbox.value);
        }
        if (bananaCheckbox.checked) {
            checkedFruits.push(bananaCheckbox.value);
        }
        if (cherryCheckbox.checked) {
            checkedFruits.push(cherryCheckbox.value);
        }
        alert('选中的水果: ' + checkedFruits.join(', '));
    }
</script>

设置复选框或单选框的选中状态也很直接:

<input type="radio" value="male" id="maleRadio">男
<input type="radio" value="female" id="femaleRadio">女
<button onclick="selectFemale()">选择女性</button>
<script>
    function selectFemale() {
        const femaleRadio = document.getElementById('femaleRadio');
        femaleRadio.checked = true;
    }
</script>

5.2 图像元素操作

5.2.1 动态更换图像源

在网页开发中,经常需要根据用户操作或某些条件动态更换图像。通过直接设置 <img> 元素的 src DOM 属性可以轻松实现。

例如,当用户点击一个按钮时,更换图像:

<img id="myImg" src="image1.jpg">
<button onclick="changeImage()">更换图像</button>
<script>
    function changeImage() {
        const img = document.getElementById('myImg');
        img.src = 'image2.jpg';
    }
</script>

5.2.2 设置图像的替代文本

为图像设置替代文本(alt 属性)对于无障碍访问非常重要。可以使用 setAttribute 或直接设置 DOM 属性的方式来设置。

例如,使用 setAttribute 方法:

<img id="myImg">
<script>
    const img = document.getElementById('myImg');
    img.setAttribute('alt', '这是一个示例图像');
</script>

使用直接设置 DOM 属性的方式:

<img id="myImg">
<script>
    const img = document.getElementById('myImg');
    img.alt = '这是一个示例图像';
</script>

5.3 样式相关操作

5.3.1 通过 className 属性操作类名

在 JavaScript 中,通过 className DOM 属性可以获取和设置元素的类名。要添加或移除类名,可以先获取当前类名,然后进行相应的拼接或拆分操作。

例如,添加一个类名:

<div id="myDiv" class="base-class"></div>
<button onclick="addClass()">添加类名</button>
<script>
    function addClass() {
        const div = document.getElementById('myDiv');
        div.className = div.className + ' new-class';
    }
</script>

移除类名:

<div id="myDiv" class="base-class new-class"></div>
<button onclick="removeClass()">移除类名</button>
<script>
    function removeClass() {
        const div = document.getElementById('myDiv');
        const classList = div.className.split(' ');
        const newClassList = [];
        for (let i = 0; i < classList.length; i++) {
            if (classList[i]!== 'new-class') {
                newClassList.push(classList[i]);
            }
        }
        div.className = newClassList.join(' ');
    }
</script>

为了更方便地操作类名,现代 JavaScript 提供了 classList 属性,它提供了 addremovetoggle 等方法。

例如,使用 classList.add 添加类名:

<div id="myDiv" class="base-class"></div>
<button onclick="addClass()">添加类名</button>
<script>
    function addClass() {
        const div = document.getElementById('myDiv');
        div.classList.add('new-class');
    }
</script>

使用 classList.remove 移除类名:

<div id="myDiv" class="base-class new-class"></div>
<button onclick="removeClass()">移除类名</button>
<script>
    function removeClass() {
        const div = document.getElementById('myDiv');
        div.classList.remove('new-class');
    }
</script>

使用 classList.toggle 切换类名(如果类名存在则移除,不存在则添加):

<div id="myDiv" class="base-class"></div>
<button onclick="toggleClass()">切换类名</button>
<script>
    function toggleClass() {
        const div = document.getElementById('myDiv');
        div.classList.toggle('new-class');
    }
</script>

5.3.2 直接设置样式属性

在某些情况下,可能需要直接设置元素的样式属性。可以通过 style DOM 属性来实现。

例如,设置 <div> 元素的背景颜色:

<div id="myDiv"></div>
<button onclick="setBackgroundColor()">设置背景颜色</button>
<script>
    function setBackgroundColor() {
        const div = document.getElementById('myDiv');
        div.style.backgroundColor ='red';
    }
</script>

需要注意的是,当设置样式属性时,属性名需要使用驼峰命名法。例如,CSS 中的 font-size 在 JavaScript 中应写成 fontSize

六、性能优化与注意事项

6.1 批量操作 DOM 属性

在对 DOM 进行多次属性操作时,为了提高性能,应该尽量批量操作,而不是每次操作都触发重排和重绘。

例如,如果要同时设置一个元素的多个样式属性,不要这样做:

<div id="myDiv"></div>
<script>
    const div = document.getElementById('myDiv');
    div.style.width = '100px';
    div.style.height = '100px';
    div.style.backgroundColor = 'blue';
</script>

而应该一次性设置:

<div id="myDiv"></div>
<script>
    const div = document.getElementById('myDiv');
    div.style.cssText = 'width: 100px; height: 100px; background-color: blue;';
</script>

6.2 事件处理中的 DOM 属性操作

在事件处理函数中操作 DOM 属性时,要注意事件的触发频率。例如,在 scroll 事件处理函数中频繁操作 DOM 属性可能会导致性能问题,因为 scroll 事件触发非常频繁。

可以使用防抖(Debounce)或节流(Throttle)技术来优化。

防抖示例(使用 Lodash 的 debounce 函数):

<div id="myDiv"></div>
<script>
    import debounce from 'lodash/debounce';
    const div = document.getElementById('myDiv');
    function updateDiv() {
        div.style.transform = 'translateX(' + window.pageXOffset + 'px)';
    }
    window.addEventListener('scroll', debounce(updateDiv, 200));
</script>

节流示例(自定义节流函数):

<div id="myDiv"></div>
<script>
    function throttle(func, delay) {
        let lastTime = 0;
        return function() {
            const now = new Date().getTime();
            if (now - lastTime >= delay) {
                func.apply(this, arguments);
                lastTime = now;
            }
        };
    }
    const div = document.getElementById('myDiv');
    function updateDiv() {
        div.style.transform = 'translateY(' + window.pageYOffset + 'px)';
    }
    window.addEventListener('scroll', throttle(updateDiv, 200));
</script>

6.3 兼容性问题

在操作 DOM 属性时,要注意不同浏览器之间的兼容性。虽然现代浏览器对 DOM 操作的支持已经比较统一,但仍然可能存在一些差异。

例如,在获取 style 属性时,某些旧版本浏览器可能返回不同格式的值。为了确保兼容性,可以使用 getComputedStyle 方法来获取元素的计算样式。

<div id="myDiv" style="width: 100px; height: 100px;"></div>
<script>
    const div = document.getElementById('myDiv');
    const style = window.getComputedStyle(div);
    console.log(style.width);
</script>

另外,在使用一些新的 DOM API 时,需要进行兼容性检测。例如,classList 属性在旧版本浏览器中可能不支持,可以通过以下方式进行兼容性处理:

<div id="myDiv" class="base-class"></div>
<script>
    if (!('classList' in document.createElement('div'))) {
        Object.defineProperty(Element.prototype, 'classList', {
            get: function() {
                const self = this;
                function updateClassList() {
                    self.__classList.value = self.className.split(' ');
                }
                const classes = this.className.split(' ');
                return {
                    value: classes,
                    add: function() {
                        for (let i = 0; i < arguments.length; i++) {
                            if (classes.indexOf(arguments[i]) === -1) {
                                classes.push(arguments[i]);
                            }
                        }
                        this.value = classes;
                        self.className = classes.join(' ');
                        updateClassList();
                    },
                    remove: function() {
                        for (let i = 0; i < arguments.length; i++) {
                            const index = classes.indexOf(arguments[i]);
                            if (index!== -1) {
                                classes.splice(index, 1);
                            }
                        }
                        this.value = classes;
                        self.className = classes.join(' ');
                        updateClassList();
                    },
                    toggle: function() {
                        for (let i = 0; i < arguments.length; i++) {
                            const index = classes.indexOf(arguments[i]);
                            if (index!== -1) {
                                classes.splice(index, 1);
                            } else {
                                classes.push(arguments[i]);
                            }
                        }
                        this.value = classes;
                        self.className = classes.join(' ');
                        updateClassList();
                    }
                };
            }
        });
    }
    const div = document.getElementById('myDiv');
    div.classList.add('new-class');
</script>

通过以上方法,可以在不同浏览器环境下,更可靠地操作 DOM 属性,提高网页的兼容性和性能。同时,在实际项目中,还应该结合项目的需求和目标浏览器版本,进行更有针对性的优化和处理。