Insert tracker directly below action row

This commit is contained in:
2026-02-26 00:14:44 +01:00
parent 2824392149
commit 498616106c

View File

@@ -242,88 +242,61 @@ function getTrackerInsertionAnchorInPost(postElement, buttonBar) {
return anchor && anchor.parentElement === postElement ? anchor : null;
}
function isNodeAfter(node, referenceNode) {
if (!node || !referenceNode || node === referenceNode) {
return false;
}
return Boolean(referenceNode.compareDocumentPosition(node) & Node.DOCUMENT_POSITION_FOLLOWING);
}
function getTopLevelAnchorWithinPost(postElement, element) {
if (!postElement || !element || !postElement.contains(element)) {
return null;
}
let anchor = element;
while (anchor.parentElement && anchor.parentElement !== postElement) {
anchor = anchor.parentElement;
}
return anchor && anchor.parentElement === postElement ? anchor : null;
}
function findCommentSectionAnchorInPost(postElement, actionAnchor = null) {
function getDirectActionAnchorInPost(postElement, buttonBar) {
if (!postElement) {
return null;
}
const selectors = [
'[aria-label*="relevantes zuerst" i]',
'[aria-label*="most relevant" i]',
'[aria-label*="neueste" i]',
'[aria-label*="newest" i]',
'[aria-roledescription*="kommentar" i]',
'[aria-roledescription*="comment" i]',
'[data-testid*="comment" i]',
'[data-scope="comment"]',
'div[role="textbox"][contenteditable="true"]',
'div[contenteditable="true"][data-lexical-editor="true"]',
'[aria-placeholder*="komment" i]',
'[aria-placeholder*="comment" i]'
const seeds = [];
const seedSelectors = [
'[data-ad-rendering-role="share_button"]',
'[data-ad-rendering-role="comment_button"]',
'[data-ad-rendering-role*="gefällt"]',
'[data-ad-rendering-role*="like_button"]',
'[data-ad-rendering-role*="reaction-like"]'
];
const anchors = new Set();
postElement.querySelectorAll(selectors.join(', ')).forEach((candidate) => {
if (!candidate || candidate.closest('.fb-tracker-ui') || !isElementVisible(candidate)) {
return;
seedSelectors.forEach((selector) => {
const candidate = postElement.querySelector(selector);
if (candidate) {
seeds.push(candidate);
}
let anchorSeed = candidate;
const isTextBox = candidate.matches('div[role="textbox"][contenteditable="true"], div[contenteditable="true"][data-lexical-editor="true"]');
if (isTextBox) {
anchorSeed = candidate.closest('form[role="presentation"]')
|| candidate.closest('form')
|| candidate;
}
let anchor = getTopLevelAnchorWithinPost(postElement, anchorSeed);
if (anchor === actionAnchor) {
anchor = anchorSeed;
}
if (!anchor || anchor === actionAnchor || !anchor.parentElement) {
return;
}
const anchorIsInsideAction = Boolean(actionAnchor && actionAnchor.contains(anchor));
if (actionAnchor && !anchorIsInsideAction && !isNodeAfter(anchor, actionAnchor)) {
return;
}
anchors.add(anchor);
});
if (!anchors.size) {
return null;
if (buttonBar && postElement.contains(buttonBar)) {
seeds.push(buttonBar);
}
return Array.from(anchors).sort((a, b) => {
if (a === b) {
return 0;
for (const seed of seeds) {
let current = seed.closest('[role="button"], button') || seed;
while (current && current !== postElement) {
if (!postElement.contains(current)) {
break;
}
return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
})[0];
const hasShare = Boolean(current.querySelector('[data-ad-rendering-role="share_button"]'));
const hasComment = Boolean(current.querySelector('[data-ad-rendering-role="comment_button"]'));
const hasLike = Boolean(
current.querySelector('[data-ad-rendering-role*="gefällt"], [data-ad-rendering-role*="like_button"], [data-ad-rendering-role*="reaction-like"]')
);
const interactionCount = [hasShare, hasComment, hasLike].filter(Boolean).length;
const hasComposer = Boolean(
current.querySelector('div[role="textbox"][contenteditable="true"], div[contenteditable="true"][data-lexical-editor="true"]')
);
if (interactionCount >= 2 && !hasComposer) {
return current;
}
current = current.parentElement;
}
}
if (buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) {
return buttonBar;
}
return getTrackerInsertionAnchorInPost(postElement, buttonBar);
}
const AI_CREDENTIAL_CACHE_TTL = 60 * 1000; // 1 minute cache
@@ -3874,35 +3847,28 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
inserted = tryInsertBeforeReelsCommentComposer();
}
const actionAnchor = getTrackerInsertionAnchorInPost(postElement, buttonBar);
const commentAnchor = findCommentSectionAnchorInPost(postElement, actionAnchor);
const directActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar);
// Strategy 1: Insert before comment section so the bar stays above lazy-loaded comments
if (!inserted && commentAnchor && commentAnchor.parentElement) {
commentAnchor.parentElement.insertBefore(container, commentAnchor);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted before comment section. ID: #' + container.id);
// Strategy 1: Insert directly below the actions row
if (!inserted && directActionAnchor && directActionAnchor.parentElement) {
directActionAnchor.parentElement.insertBefore(container, directActionAnchor.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted directly below actions. ID: #' + container.id);
inserted = true;
}
// Strategy 2: Insert right after the action block inside the current post container
if (!inserted && actionAnchor && actionAnchor.parentElement) {
actionAnchor.parentElement.insertBefore(container, actionAnchor.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after in-post action block. ID: #' + container.id);
inserted = true;
}
// Strategy 3: Insert directly after the button bar if still inside the post container
// Strategy 2: Insert directly after the detected button bar if still inside the post container
if (!inserted && buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar (in-post fallback). ID: #' + container.id);
inserted = true;
}
// Strategy 4: Legacy fallback for old variants where the interaction bar is outside the post container
// Strategy 3: Legacy fallback for old variants where the interaction bar is outside the post container
if (!inserted && buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
const grandParent = buttonBar.parentElement.parentElement;
grandParent.insertBefore(container, buttonBar.parentElement.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after external button bar parent (legacy). ID: #' + container.id);
inserted = true;
}
// Strategy 5: Append to post element
// Strategy 4: Append to post element
if (!inserted) {
postElement.appendChild(container);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id);
@@ -3933,12 +3899,9 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
console.log('[FB Tracker] Post #' + postNum + ' - UI was removed, re-inserting...');
observer.disconnect();
// Try to re-insert
const currentActionAnchor = getTrackerInsertionAnchorInPost(postElement, buttonBar);
const currentCommentAnchor = findCommentSectionAnchorInPost(postElement, currentActionAnchor);
if (currentCommentAnchor && currentCommentAnchor.parentElement) {
currentCommentAnchor.parentElement.insertBefore(container, currentCommentAnchor);
} else if (currentActionAnchor && currentActionAnchor.parentElement) {
currentActionAnchor.parentElement.insertBefore(container, currentActionAnchor.nextSibling);
const currentDirectActionAnchor = getDirectActionAnchorInPost(postElement, buttonBar);
if (currentDirectActionAnchor && currentDirectActionAnchor.parentElement) {
currentDirectActionAnchor.parentElement.insertBefore(container, currentDirectActionAnchor.nextSibling);
} else if (buttonBar && buttonBar.parentElement && postElement.contains(buttonBar.parentElement)) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
} else if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {