我正在尝试为 Airbnb 构建一个 chrome 扩展。目前我正在尝试将一个 div(反应)附加到每个
listingDiv
的子组件,因为 airbnb 在网格中呈现它们。
一般实现这个我:
抓取脚本执行时页面上存在的任何内容
脚本执行后立即附加一个 MutationObserver。
getElementByClassName
找到子组件(价格容器),我可以在其中附加我插入到带有扩展名的页面中的div
。
我遇到了很多问题,但通常是:
a) 当用变异观察器抓取一个元素时,该元素没有完全完成——有时子组件还没有被渲染。
b) 使用 setInterval 持续检查
listingDiv
表明该元素实际上是静态的,并且子组件永远不会附加到它。
c) 我不确定我使用 MutationObserver 的方法是否正确——有些元素在突变时似乎不会触发(可能是因为它们是静态的)。
这是我的代码(目前充满了一些调试片段):
import React from "react";
import { createRoot } from 'react-dom/client';
// //---Initiate content-background script communication channel---
// // content-script.js
// let myPort = chrome.runtime.connect({name:"port-from-cs"});
// myPort.postMessage({greeting: "hello from content script"});
// myPort.onMessage.addListener((m) => {
// console.log("In content script, received message from background script: ");
// console.log(m.greeting);
// });
//---Add 'request-flights-button' buttons to the page---
// const reactComponent = React.createElement('div');
function ReactComponent({idNumber}) {
return (
<div id={"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" + idNumber.toString()}>test rabm</div>
)
}
//---Grab all the listings on a page and iterate to generate listing information for each---
//---Render the 'request-flights-button' button on each listing div---
function renderFlightPriceRequestButton(listingDiv: HTMLDivElement, extensionListingId: string): void {
const listingPriceClassName = '_i5duul';
const priceContainer = listingDiv.getElementsByClassName(listingPriceClassName)[0];
if (priceContainer === undefined) {
setInterval(() => {
console.log(listingDiv)
console.log(listingDiv.getElementsByClassName(listingPriceClassName))
}, 1000)
}
const attachmentPoint = document.createElement('span');
attachmentPoint.id = 'request-flights-button-' + extensionListingId;
priceContainer.appendChild(attachmentPoint);
createRoot(attachmentPoint).render(<ReactComponent idNumber={extensionListingId}/>);
};
const configForListings = { attributes: true, childList: true, subtree: true };
// Callback function to execute when mutations are observed
function observerCallBackFindListings(mutationsList: MutationRecord[]): void {
const listingClassName = 'c4mnd7m';
for(let mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0){
mutation.addedNodes.forEach(node => {
if(node instanceof Element){
const candidateListingDivs = node.querySelectorAll('.' + listingClassName) as NodeListOf<HTMLDivElement>;
if (candidateListingDivs.length > 0) {
for (let candidateListingDiv of candidateListingDivs){
console.log('calling from observer')
const extensionListingId = grabExtensionListingId(candidateListingDiv);
renderFlightPriceRequestButton(candidateListingDiv, extensionListingId);
};
}
}
})
}
}
};
function grabExtensionListingId(listingDiv: HTMLDivElement): string {
let listingId;
try {
const listingUrlMetaTag = listingDiv.querySelector('meta[itemprop="url"]') as HTMLMetaElement;
const listingDivUrl = listingUrlMetaTag.getAttribute('content');
listingId = listingDivUrl?.split('/')[2].slice(0, 8);
if(listingId === undefined){
throw new Error('listingId is undefined');
} else {
return listingId;
}
} catch (error) {
console.log(error);
return 'error';
}
}
// Create an observer instance linked to the callback function
const observerFindListings = new MutationObserver(observerCallBackFindListings);
function findListings (){
const listingClassName = 'c4mnd7m';
const existingListingDivs = document.getElementsByClassName(listingClassName) as HTMLCollectionOf<HTMLDivElement>;
for (const listingDiv of existingListingDivs){
const extensionListingId = grabExtensionListingId(listingDiv);
renderFlightPriceRequestButton(listingDiv, extensionListingId);
};
observerFindListings.observe(document, configForListings);
}
findListings();
//---Send listing information on 'request-flights-button' button click---
//---Receive information from the background script---
//---Display flight information on the page---