Skip to content

Instantly share code, notes, and snippets.

@xinzhi
Created April 12, 2023 15:20
Show Gist options
  • Save xinzhi/b56e6f03bcf320818a1506399a10368a to your computer and use it in GitHub Desktop.
Save xinzhi/b56e6f03bcf320818a1506399a10368a to your computer and use it in GitHub Desktop.
自动展开显示 Twitter 图片 ALT 文本的扩展脚本,推荐搭配 Arc 浏览器使用
// 1. 打开 Arc 浏览器访问 Twitter 网站;
// 2. 左侧面板右下角点击 New Boost - Inject - A specific website(确认应用范围是 twitter.com)
// 3. 请空默认代码,粘贴本页面代码即可。
// 效果测试地址: https://twitter.com/ciguleva/status/1637233764656107520?s=20
const tweetSelector = 'article[data-testid="tweet"]';
const tweetTextSelector = 'div[data-testid="tweetText"]';
const tweetPhotoSelector = 'div[data-testid^="tweetPhoto"]';
const appendAltText = (tweet, altText) => {
const tweetTextElement = tweet.querySelector(tweetTextSelector);
const existingList =
tweetTextElement?.querySelector("ol") ??
(() => {
const newList = document.createElement("ol");
tweetTextElement.appendChild(newList);
return newList;
})();
const altPromptElement =
tweetTextElement.querySelector(".alt-prompt") ??
(() => {
const altPromptText = document.createTextNode("\n\nALTs: ");
const newAltPrompt = document.createElement("span");
newAltPrompt.className = "alt-prompt";
newAltPrompt.appendChild(altPromptText);
tweetTextElement.insertBefore(newAltPrompt, existingList);
return newAltPrompt;
})();
const listItem = document.createElement("li");
listItem.textContent = altText;
existingList.appendChild(listItem);
};
const tweetContainsMedia = (tweet) =>
tweet.querySelector(tweetPhotoSelector) !== null;
const processTweet = (tweet) => {
if (tweet.hasAttribute("data-processed") || !tweetContainsMedia(tweet)) {
return;
}
tweet.setAttribute("data-processed", "true");
tweet.querySelectorAll(tweetPhotoSelector).forEach((photo) => {
const altText = photo.getAttribute("aria-label");
if (altText && altText.length >= 10) {
appendAltText(tweet, altText);
}
});
};
const handleIntersection = (entries) => {
entries.forEach((entry) => {
if (
entry.isIntersecting &&
entry.target.matches(tweetSelector) &&
!entry.target.hasAttribute("data-processed") &&
tweetContainsMedia(entry.target)
) {
processTweet(entry.target);
}
});
};
const processAddedNode = (addedNode) => {
if (addedNode.nodeType !== Node.ELEMENT_NODE) {
return;
}
addedNode.querySelectorAll(tweetSelector).forEach((tweet) => {
processTweet(tweet);
observer.observe(tweet);
});
};
const observer = new IntersectionObserver(handleIntersection, {
threshold: 0.5,
});
const targetNode = document.body;
const observerConfig = { childList: true, subtree: true };
const mutationObserver = new MutationObserver((mutationsList) =>
mutationsList.forEach(({ addedNodes }) =>
addedNodes.forEach(processAddedNode)
)
);
document.querySelectorAll(tweetSelector).forEach((tweet) => {
processTweet(tweet);
observer.observe(tweet);
});
mutationObserver.observe(targetNode, observerConfig);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment