詳解如何使用JavaScript獲取自動(dòng)消失的聯(lián)想詞
我問(wèn)題
前幾天在做數(shù)據(jù)分析時(shí),我嘗試獲取某網(wǎng)站上輸入搜索詞后的聯(lián)想詞,輸入搜索詞后會(huì)彈出一個(gè)顯示聯(lián)想詞的框。有趣的是,當(dāng)我嘗試通過(guò)按F12定位這個(gè)彈框在HTML中的位置時(shí),輸入框失去焦點(diǎn)后,聯(lián)想詞彈框就自動(dòng)消失了。我觀察到 HTML 代碼中
div元素也從代碼中消失了。這種情況下,我該如何才能準(zhǔn)確地定位這個(gè)元素彈框并獲取其中的聯(lián)想詞呢?

解決方案
方法一:查找DOM節(jié)點(diǎn)
這算是最簡(jiǎn)單的操作,通過(guò)調(diào)試框可以看到網(wǎng)頁(yè)dom結(jié)構(gòu)的變化,當(dāng)觸發(fā)聯(lián)想詞彈框時(shí),記下彈框?qū)?yīng)標(biāo)簽的類名或者id,然后查找得到這個(gè)標(biāo)簽的html代碼,從中解析出聯(lián)想詞。
const elementByClassName = document.querySelector(".luoxiaodou");
if (elementByClassName) {
const htmlContent = elementByClassName.innerHTML;
console.log(htmlContent);
}
輸入框獲取焦點(diǎn)時(shí),聯(lián)想詞彈框出現(xiàn),輸入框失去焦點(diǎn)時(shí),聯(lián)想詞彈框消失。咱們先給輸入框添加焦點(diǎn),彈框出現(xiàn)時(shí),運(yùn)行上面的代碼獲取節(jié)點(diǎn)對(duì)應(yīng)的html內(nèi)容。
這個(gè)想法很好,但是這里有一個(gè)問(wèn)題:就是元素的加載需要時(shí)間,需要請(qǐng)求服務(wù)端獲取數(shù)據(jù),然后再加載出來(lái)。,如果是輸入框獲取焦點(diǎn)后立即去查找元素,大概率獲取的結(jié)果是空,所以這中間要有延遲的時(shí)間,咱們就說(shuō)這坑大家有沒(méi)有踩過(guò)吧。
方法二:使用MutationObserver來(lái)監(jiān)視DOM變化
我還有兩個(gè)想法,第一個(gè)是:咱們能不能攔截一下網(wǎng)絡(luò),然后從服務(wù)器的響應(yīng)里頭撈點(diǎn)聯(lián)想詞出來(lái)?我個(gè)人覺(jué)得這招兒挺靠譜的,但目前還沒(méi)來(lái)得及試試。等我嘗試之后寫一下關(guān)于監(jiān)聽(tīng)網(wǎng)絡(luò)請(qǐng)求獲取聯(lián)想詞的內(nèi)容,咱們就先記個(gè)賬,到時(shí)候一定補(bǔ)上。
另一種方式就是使用MutationObserver來(lái)監(jiān)視DOM變化,可以在控制臺(tái)中編寫一個(gè)JavaScript函數(shù),當(dāng)包含特定類的元素出現(xiàn)在DOM中時(shí),打印出該元素的HTML內(nèi)容。為此,你可以使用MutationObserver來(lái)監(jiān)視DOM變化。
而我選擇了另一種可能的解決方案,就是利用MutationObserver這個(gè)JavaScript API。一旦檢測(cè)到DOM中有特定類的元素出現(xiàn),就立即打印出它的HTML內(nèi)容。MutationObserver 是 JavaScript 自帶的 API,它用于監(jiān)視DOM樹(shù)的變化。這是瀏覽器原生支持的功能,包括我們常用的Chrome在內(nèi)。正好在插件開(kāi)發(fā)中使用。
咱們可以創(chuàng)建一個(gè)函數(shù),傳參是標(biāo)簽選擇器和一個(gè)回調(diào)函數(shù),當(dāng)獲取到變化的網(wǎng)頁(yè)時(shí),調(diào)用回調(diào)函數(shù)解析獲取聯(lián)想詞。這里使用一個(gè)map集合,可以保證管理多個(gè)監(jiān)視器,以下是實(shí)現(xiàn)代碼:
const observers = new Map()
/**
* 啟動(dòng)監(jiān)視指定選擇器的元素變化。
* @param {string} selector - CSS選擇器,可以是類名(以`.`開(kāi)頭)或ID(以`#`開(kāi)頭)。
* @param {function} callback - 回調(diào)函數(shù),當(dāng)符合選擇器的元素添加到DOM時(shí)調(diào)用。
*/
function startObserving(selector, callback) {
/**
* 觀察者回調(diào)函數(shù),監(jiān)視DOM樹(shù)的變化。
* @param {MutationRecord[]} mutationsList - DOM變化記錄的列表。
* @param {MutationObserver} observer - 觀察者實(shí)例。
*/
const observerCallback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (selector.startsWith('#') && node.id === selector.slice(1)) {
callback(node.outerHTML)
} else if (selector.startsWith('.') && node.classList.contains(selector.slice(1))) {
callback(node.outerHTML)
}
}
})
}
}
}
// 創(chuàng)建一個(gè)觀察者實(shí)例并開(kāi)始觀察指定的目標(biāo)節(jié)點(diǎn)和配置
const observer = new MutationObserver(observerCallback)
// 觀察配置,配置對(duì)象指定我們希望觀察子節(jié)點(diǎn)的變化(`childList: true`)以及觀察整個(gè)子樹(shù)中的變化(`subtree: true`)
const config = { childList: true, subtree: true }
// 觀察整個(gè)文檔,所以將目標(biāo)節(jié)點(diǎn)設(shè)置為`document.body`。
observer.observe(document.body, config)
// 將觀察者存儲(chǔ)在Map中
observers.set(selector, observer)
// 設(shè)置定時(shí)器,指定時(shí)間后自動(dòng)停止觀察并刪除觀察者,回收資源
setTimeout(() => {
stopObserving(selector)
}, 5 * 60 * 1000)
}
/**
* 停止監(jiān)視指定選擇器的元素變化。
* @param {string} selector - CSS選擇器,可以是類名(以`.`開(kāi)頭)或ID(以`#`開(kāi)頭)。
*/
function stopObserving(selector) {
const observer = observers.get(selector);
if (observer) {
observer.disconnect();
observers.delete(selector);
}
}
export { startObserving, stopObserving };
然后就是在使用的地方,導(dǎo)入這些方法,傳一下參數(shù)就可以了,有一點(diǎn)要注意:回調(diào)函數(shù)中的參數(shù)是獲取到的網(wǎng)頁(yè)信息。
import { startObserving, stopObserving } from './element-observer.js';
// 通過(guò)類名啟動(dòng)監(jiān)視
startObserving('.xiaodou-class', (htmlContent) => {
console.log('Element with class added:', htmlContent);
});
// 通過(guò)ID啟動(dòng)監(jiān)視
startObserving('#xiaodou-id', (htmlContent) => {
console.log('Element with ID added:', htmlContent);
});
// 停止監(jiān)視
setTimeout(() => {
stopObserving('.xiaodou-class');
stopObserving('#xiaodou-id');
}, 6000);
你是不是以為這就結(jié)束?我當(dāng)時(shí)就是這么想的,但是還是有問(wèn)題:從第二次開(kāi)始獲取到的內(nèi)容是上一個(gè)輸入詞的聯(lián)想詞。假設(shè)第一次輸入“貓咪”,然后獲取的“貓咪”的聯(lián)想詞,第二次輸入“狗狗”,但是獲取的還是“貓咪”的聯(lián)想詞,第三次輸入“兔兔”,這次獲取的是“狗狗”的聯(lián)想詞,這是哪里出問(wèn)題了呢?
接著咱們來(lái)分析一下原因,首先觀察一下聯(lián)想詞彈框的變化,比如下圖,修改”貓咪貍花貓“到”貓咪貍“的這個(gè)過(guò)程中,聯(lián)想詞彈框先出來(lái)跟”貓咪貍花貓“相關(guān)的內(nèi)容, 再請(qǐng)求服務(wù)器獲取”貓咪貍“的聯(lián)想詞,再更新彈框內(nèi)容。

當(dāng)頁(yè)面變動(dòng)時(shí),監(jiān)控頁(yè)面變化的函數(shù)會(huì)立即執(zhí)行,這個(gè)執(zhí)行速度是優(yōu)于請(qǐng)求服務(wù)器更新彈框內(nèi)容的速度,所以這里導(dǎo)致獲取的內(nèi)容是是上一個(gè)聯(lián)想詞的。
發(fā)現(xiàn)了問(wèn)題,就好修改了,可以嘗試在 MutationObserver 回調(diào)函數(shù)中使用 setTimeout,以確保 DOM 完全渲染后再獲取 outerHTML。
/**
* 觀察者回調(diào)函數(shù),監(jiān)視DOM樹(shù)的變化。
* @param {MutationRecord[]} mutationsList - DOM變化記錄的列表。
* @param {MutationObserver} observer - 觀察者實(shí)例。
*/
const observerCallback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (selector.startsWith('#') && node.id === selector.slice(1)) {
// 使用 setTimeout 確保 DOM 完全渲染后獲取 outerHTML
setTimeout(() => callback(node.outerHTML), 1000);
} else if (selector.startsWith('.') && node.classList.contains(selector.slice(1))) {
// 使用 setTimeout 確保 DOM 完全渲染后獲取 outerHTML
setTimeout(() => callback(node.outerHTML), 1000);
}
}
});
}
}
這里確實(shí)解決了問(wèn)題,但是我還有一個(gè)疑問(wèn):為什么在修改”貓咪貍花貓“到”貓咪貍“的這個(gè)過(guò)程中,彈框內(nèi)容有兩次變化,觀察者只觸發(fā)了一次?,在上面的觀察者配置是希望觀察子節(jié)點(diǎn)的變化以及觀察整個(gè)子樹(shù)中的變化,這個(gè)變化不包含節(jié)點(diǎn)內(nèi)容的變化,不知道這樣理解對(duì)不對(duì)?
以上就是詳解如何使用JavaScript獲取自動(dòng)消失的聯(lián)想詞的詳細(xì)內(nèi)容,更多關(guān)于JavaScript獲取聯(lián)想詞的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
element-table表格中插入顏色塊顯示數(shù)據(jù)狀態(tài)的示例代碼
這篇文章主要介紹了element-table表格中插入顏色塊顯示數(shù)據(jù)狀態(tài),代碼部分分為dom部分和data部分及css部分,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12
使用 JavaScript 進(jìn)行函數(shù)式編程 (一) 翻譯
本文是函數(shù)式編程系列的第一篇文章。這里我會(huì)簡(jiǎn)要介紹一下編程范式,然后會(huì)直接介紹使用 Javascript 進(jìn)行函數(shù)式編程的概念,因?yàn)?JavsScript 是最被認(rèn)可的函數(shù)式程序語(yǔ)言之一。我們鼓勵(lì)讀者通過(guò)參考資料部分進(jìn)一步了解這一迷人的概念2015-10-10
uniapp通過(guò)概率實(shí)現(xiàn)隨機(jī)抽獎(jiǎng)的項(xiàng)目實(shí)踐
在很多電商平臺(tái)或者活動(dòng)中,都會(huì)有類似抽獎(jiǎng)贏優(yōu)惠券的功能,本文主要介紹了uniapp通過(guò)概率實(shí)現(xiàn)隨機(jī)抽獎(jiǎng)的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04
基于Bootstrap實(shí)現(xiàn)的下拉菜單手機(jī)端不能選擇菜單項(xiàng)的原因附解決辦法
小編使用bootstrap做的下拉菜單在電腦瀏覽器中可以正常使用,在手機(jī)瀏覽器中能彈出下拉列表卻不能選擇列表中的菜單項(xiàng),怎么回事,如何解決呢?下面小編給大家分享下具體原因及解決辦法,一起看下吧2016-07-07

