那是个夜黑风高的夜晚,我遇到了一个按钮:html
<button type="submit">搜索</button>
嗯,我要选中它,我敲下了一行代码:程序员
const submitButton = document.querySelector('button[type="submit"]');
这对于精通 document.querySelector
的函数名书写方式的我来讲,简直就像吃下四两饭同样简单!数组
可是。浏览器
咱们知道,document.querySelector
接收一个选择器字符串,返回第一个匹配的 DOM
元素,因此若是页面上只有一个 button[type="submit"]
或者这个 button[type="submit"]
在 html 中是第一个时,我这个方法是无懈可击的。app
而后,我发现页面上居然存在两个 button[type="submit"]
类型的按钮(黑人问号???)。函数
我对比了一下差别:oop
<button type="submit">提交</button> <button type="submit">搜索</button>
先不八卦为何页面上有两个差很少的按钮,但能初步断定的是,他们长得很像,嗯。测试
那么,问题来了,我怎么选中那个搜索框,我把问题抛给了自觉得是的本身,获得了几个回答。prototype
分身 A:改用 selectorSelectorAll
拿到第二个不就好了嘛!code
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 元素有哪些方法吧。
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
指代网页中有效的 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');
用法和 getElementsByClassName 类似
, 返回一个 HTMLCollection
。
用法和 getElementsByClassName 类似
, 返回一个 HTMLCollection
。
再熟悉不过的接口了,传入 CSS 选择器,返回第一个找到的元素。
以上返回数组的方法,getElementsByName
、getElementsByTagName
、getElementsByTagNameNS
、querySelectorAll
返回的都是 Array-like 的 HTMLCollection
。
要像对数组同样对 HTMLCollection
进行操做(使用数组的 forEach
、filter
等方法),则须要将他们转化为数组:
const buttonCollection = document.getElementsByClassName('submit'); // const buttonArray = Array.prototype.slice.call(buttonCollection); const buttonArray = [].slice.call(buttonCollection); buttonArray.forEach(item => { // do something });
注意,对于都是返回 HTMLCollection
的方法,与 querySelector
不一样的是,getElementsByName
、getElementsByClassName
、getElementsByTagName
返回的 HTMLCollection
是一个引用类型的 HTMLCollection
。
也就是:
querySelectorAll
获取的是当时文档中匹配选择器的 DOM 元素,不包含后面插入到文档中的元素,若是文档有更新,则须要从新获取getElementsByName
、getElementsByClassName
和 getElementsByTagName
则能随时反映文档中匹配对应规则的元素,包含后面插入到文档中的元素,若是文档有更新,不须要从新获取这个说法有点相似拷贝和引用的关系。
话很少说,咱们直接写点代码作个测试:
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] */
回到本文讨论的问题,貌似浏览器原生并无提供相似 document.getElementByInnerText
?
找了一圈貌似没有,那只能借用 Javascript 了。
回忆一下须要找到的元素是:
<button type="submit">搜索</button>
可使用 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 如其名获取内部 HTML 片断,包含标签;
textContent 相似 innerText,不过会在去掉标签后将嵌套的纯文本内容也获取出来。