资讯专栏INFORMATION COLUMN

获取网页指定元素的原生方法回顾

URLOS / 722人阅读

摘要:浏览器原生提供的几个找到元素的方法为网页全局唯一。匹配包含的元素匹配包含和的元素级联用法和相似返回一个。以上返回数组的方法,返回的都是的。

那是个夜黑风高的夜晚,我遇到了一个按钮:

嗯,我要选中它,我敲下了一行代码:

const submitButton = document.querySelector("button[type="submit"]");

这对于精通 document.querySelector 的函数名书写方式的我来说,简直就像吃下四两饭一样简单!

但是。

我们知道,document.querySelector 接收一个选择器字符串,返回第一个匹配的 DOM 元素,所以如果页面上只有一个 button[type="submit"] 或者这个 button[type="submit"] 在 html 中是第一个时,我这个方法是无懈可击的。

然后,我发现页面上竟然存在两个 button[type="submit"] 类型的按钮(黑人问号???)。

我对比了一下差异:


先不八卦为什么页面上有两个差不多的按钮,但能初步判定的是,他们长得很像,嗯。

那么,问题来了,我怎么选中那个搜索框,我把问题抛给了自以为是的自己,得到了几个回答。

分身 A:改用 selectorSelectorAll 拿到第二个不就行了嘛!

const allSubmitButton = document.querySelectorAll("button[type="submit"]");
const submitButton = allSubmitButton[1];

这方法貌似不错,但万一这两个按钮对调位置了怎么办?

分身 B: 那选择器写全一点,也匹配『搜索』这两个字不就可以把『提交』那个按钮给拍除掉了嘛!

const submitButton = document.querySelector("button[type="submit"][innerText="搜索"]");

打印出来一看,嗯,怎么输出是 null???

分身 B: 那个,那个。。。innerText 是我编出来的,我不知道正确的要怎么写。

嗯,虽然 B 写错了语法(哪个程序员会不写错语法?),但限定了两个条件,type="submit"innerText="搜索", 能唯一匹配搜索按钮了,这个思路是对的。

那么,正确的语法是什么呢?

苦苦搜索半天,无果。

分身 C: 那就是写不出这样的选择器吧,还是用 JS 来做吧 :)

分身 A: 这么简单的需求,CSS 是万能的,为什么要用 Javascript?

分身 B: 对,Hail CSS!

。。。那我们就借此机会温习一下找到一个 DOM 元素有哪些方法吧。

浏览器原生提供的几个找到 DOM 元素的方法 document.getElementById

Id 为网页全局唯一。

document.getElementById("id");

注意,与其他方法不一样,getElementById 只能被全局对象 document 调用,否则会报错:

document.getElementById("parentId").getElementById("childId");
// Uncaught TypeError: document.getElementById(...).getElementById is not a function

因为 Id 是全局唯一的,所以上面第二种写法自然也显得没有必要。

Id 对大小写敏感(除非文档被声明为特殊类型如: XHTML, XUL),所以以下两种写法并不等同:

document.getElementById("id");
documen.getElementById("ID");
element.getElementsByClassName

这里的 element 指代网页中有效的 DOM 元素,包含 document

返回一个 HTMLCollection

// 匹配 class 包含 "submit" 的元素
document.getElementsByClassName("submit");

// 匹配 class 包含 "submit" 和 "button" 的元素
document.getElementsByClassName("submit button");

// 级联
cons selectedElement = document.getElementById("app-container");
selectedElement.getElementsByClassName("submit button");
element.getElementsByName

用法和 getElementsByClassName 相似, 返回一个 HTMLCollection

element.getElementsByTagName

用法和 getElementsByClassName 相似, 返回一个 HTMLCollection

element.querySelector

再熟悉不过的接口了,传入 CSS 选择器,返回第一个找到的元素。

element.querySelectorAll

以上返回数组的方法,getElementsByNamegetElementsByTagNamegetElementsByTagNameNSquerySelectorAll 返回的都是 Array-like 的 HTMLCollection

要像对数组一样对 HTMLCollection 进行操作(使用数组的 forEachfilter 等方法),则需要将他们转化为数组:

const buttonCollection = document.getElementsByClassName("submit");

// const buttonArray = Array.prototype.slice.call(buttonCollection);
const buttonArray = [].slice.call(buttonCollection);

buttonArray.forEach(item => {
    // do something
});
querySelector 与 getElementsByName、getElementsByClassName 和 getElementsByTagName 的细微差异

注意,对于都是返回 HTMLCollection 的方法,与 querySelector 不同的是,getElementsByNamegetElementsByClassNamegetElementsByTagName 返回的 HTMLCollection 是一个引用类型的 HTMLCollection

也就是:

querySelectorAll 获取的是当时文档中匹配选择器的 DOM 元素,不包含后面插入到文档中的元素,如果文档有更新,则需要重新获取

getElementsByNamegetElementsByClassNamegetElementsByTagName 则能随时反映文档中匹配对应规则的元素,包含后面插入到文档中的元素,如果文档有更新,不需要重新获取

这个说法有点类似拷贝和引用的关系。

话不多说,我们直接写点代码做个测试:

const createElement = (func) => {
    const key = "myCustomElement";
    let element;
    switch (func) {
        case "getElementsByName":
            element = document.createElement("div");
            element.setAttribute("name", key);
            break;
        case "getElementsByClassName":
            element = document.createElement("div");
            element.setAttribute("class", key);
            break;
        case "getElementsByTagName":
            element = document.createElement(key);
            break;
        case "querySelectorAll":
            element = document.createElement("div");
            element.className = key;
            break;
        default:
            element = document.createElement("div");
    }

    return element;
}

const getCollection = (root, func) => {
    const key = "myCustomElement";
    let element;
    if (func === "getElementsByName") {
        return document.getElementsByName(key);
    }
    if (func === "querySelectorAll") {
        return root.querySelectorAll(`.${key}`);
    }
    return root[func](key);
}


const testFunc = (func) => {
    const result = [];

    // 避免 getElementsByClassName 和 querySelectorAll 在统一环境的影响,创建一个独立的容器测试
    const container = document.createElement("div");
    document.body.append(container);

    // 1. 插入一个
    container.append(createElement(func));

    // 2. 看下现在有多少个
    const collection = getCollection(container, func);
    result.push(collection.length);

    // 3. 继续插入一个
    container.append(createElement(func));

    // 4. 看下现在有多少个
    result.push(collection.length);

    return result;
};

console.log("getElementsByName", testFunc("getElementsByName")); // [1, 2]
console.log("getElementsByClassName", testFunc("getElementsByClassName")); // [1, 2]
console.log("getElementsByTagName", testFunc("getElementsByTagName")); // [1, 2]
console.log("querySelectorAll", testFunc("querySelectorAll")); // [1, 1] // 注意这个输出

// 输出的是:
/*
    getElementsByName [1, 2]
    getElementsByClassName [1, 2]
    getElementsByTagName [1, 2]
    querySelectorAll [1, 1]
*/
还有什么方法可以根据 innerText 找到指定元素

回到本文讨论的问题,貌似浏览器原生并没有提供类似 document.getElementByInnerText

找了一圈貌似没有,那只能借用 Javascript 了。

回忆一下需要找到的元素是:

使用原生 Javascript

可以使用 querySelectorAll

let foundElement = null;
const elementsCollection = document.querySelectorAll("button[type="submit"]");
const elementArray = [].slice.call(elementsCollection);
/*
// 使用 Array.forEach 遍历,缺点是找到后没法 break
elementArray.forEach(element => {
    if (element.innerText.trim() === "搜索") {
        foundElement = element;
    }

    // 或者使用正则
    /*
    if (/^s{0,}搜索s{0,}$/.test(element.innerText)) {
        foundElement = element;
    }
    */
});
*/

/*
// 或使用 for-loop 找到后提前 break
const len = inputElementArray.length;
for (let i = 0; i < len; i++) {
    if (elementArray[i].innerText.trim()) {
        foundElement = elementArray[i];
        break;
    }
}
*/

// 或使用 filter
const foundElementArray = elementArray.filter(element => element.innerText.trim() === "搜索");
if (foundElementArray.length) {
    foundElement = foundElementArray[0];
}
题外话:innerText 与 innerHTML、textContent 的差异?

简而言之:

innerText 获取元素第一层的文本内容,不包含标签;
innerHTML 如其名获取内部 HTML 片段,包含标签;
textContent 类似 innerText,不过会在去掉标签后将嵌套的纯文本内容也获取出来。

MDN textContent"s difference from innerText and innerHTML

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89049.html

相关文章

  • jQuery笔记总结篇

    摘要:希望在做所有事情之前,操作文档。不受层级限制子选择器在给定的父元素下匹配所有子元素。相邻选择器匹配所有紧接在元素后的元素。判断当前对象中的某个元素是否包含指定类名,包含返回,不包含返回下标过滤器精确选出指定下标元素获取第个元素。 原文链接 http://blog.poetries.top/2016... 首先,来了解一下jQuery学习的整体思路 showImg(https://seg...

    NoraXie 评论0 收藏0
  • 2017前端发展回顾

    摘要:前端开发在年依然持续火热,本文将对热点事件做一个总结。版的和协议在前端领域,一直独占鳌头。年又发布了一个重大的版本更新。主要是配合使用了服务工作线程。而且还提供了供前端开发者接入。快速发布了和在悄悄地跳过之后,在月号正式发布。 译者按: 老技术日趋成熟,新技术层出不穷。 原文: A recap of front-end development in 2017 译者: Fundebu...

    lmxdawn 评论0 收藏0
  • 我们来翻翻元素样式族谱-getComputedStyle

    摘要:大家应该非常熟悉的方法那么如何在不引用的情况下同样实现这个功能呢本文就介绍使用原生来获取样式的方法作者原文链接我们来翻翻元素样式的族谱是什么方法可以获取当前元素所有最终使用的属性值返回的是一个样式声明对象只读也就是说获取到的不仅仅是我们自 大家应该非常熟悉jQuery的css()方法,那么如何在不引用jQuery的情况下同样实现这个功能呢?本文就介绍使用原生JS来获取样式的方法. 作者...

    graf 评论0 收藏0
  • 我们来翻翻元素样式族谱-getComputedStyle

    摘要:大家应该非常熟悉的方法那么如何在不引用的情况下同样实现这个功能呢本文就介绍使用原生来获取样式的方法作者原文链接我们来翻翻元素样式的族谱是什么方法可以获取当前元素所有最终使用的属性值返回的是一个样式声明对象只读也就是说获取到的不仅仅是我们自 大家应该非常熟悉jQuery的css()方法,那么如何在不引用jQuery的情况下同样实现这个功能呢?本文就介绍使用原生JS来获取样式的方法. 作者...

    shengguo 评论0 收藏0
  • 我们来翻翻元素样式族谱-getComputedStyle

    摘要:大家应该非常熟悉的方法那么如何在不引用的情况下同样实现这个功能呢本文就介绍使用原生来获取样式的方法作者原文链接我们来翻翻元素样式的族谱是什么方法可以获取当前元素所有最终使用的属性值返回的是一个样式声明对象只读也就是说获取到的不仅仅是我们自 大家应该非常熟悉jQuery的css()方法,那么如何在不引用jQuery的情况下同样实现这个功能呢?本文就介绍使用原生JS来获取样式的方法. 作者...

    My_Oh_My 评论0 收藏0

发表评论

0条评论

URLOS

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<