Stabilize tracker bar placement above Facebook action row

This commit is contained in:
2026-02-26 08:57:41 +01:00
parent 4782f09dfc
commit 26c552aab5

View File

@@ -368,6 +368,82 @@ function countActionRoleSignals(node) {
return [hasLike, hasComment, hasShare].filter(Boolean).length; return [hasLike, hasComment, hasShare].filter(Boolean).length;
} }
function hasCommentSortSignal(node) {
if (!node || !node.querySelector) {
return false;
}
return Boolean(
node.querySelector(
'[aria-label*="relevantes zuerst" i], [aria-label*="most relevant" i], [aria-label*="neueste" i], [aria-label*="newest" i]'
)
);
}
function isActionRowShapeCandidate(postElement, candidate) {
if (!postElement || !candidate || !candidate.isConnected || !postElement.contains(candidate)) {
return false;
}
if (candidate.closest('.fb-tracker-ui')) {
return false;
}
if (
hasCommentComposerSignal(candidate)
|| hasCommentContentSignal(candidate)
|| hasCommentSortSignal(candidate)
) {
return false;
}
const buttonCount = candidate.querySelectorAll('[role="button"], button').length;
if (buttonCount < 3 || buttonCount > 24) {
return false;
}
return true;
}
function isCompactActionBarCandidate(postElement, candidate) {
if (!isActionRowShapeCandidate(postElement, candidate)) {
return false;
}
const actionRoleSignals = countActionRoleSignals(candidate);
if (actionRoleSignals >= 2) {
return true;
}
return hasInteractionButtons(candidate);
}
function normalizeActionBarCandidate(postElement, candidate) {
if (!postElement || !candidate || !candidate.isConnected || !postElement.contains(candidate)) {
return null;
}
let fallback = null;
let current = candidate;
for (let depth = 0; depth < 12 && current && current !== postElement; depth++) {
if (isCompactActionBarCandidate(postElement, current)) {
return current;
}
if (!fallback && isActionRowShapeCandidate(postElement, current)) {
fallback = current;
}
current = current.parentElement;
}
if (fallback) {
return fallback;
}
return isReliableActionBarCandidate(postElement, candidate) ? candidate : null;
}
function isReliableActionBarCandidate(postElement, candidate) { function isReliableActionBarCandidate(postElement, candidate) {
if (!postElement || !candidate || !candidate.isConnected || !postElement.contains(candidate)) { if (!postElement || !candidate || !candidate.isConnected || !postElement.contains(candidate)) {
return false; return false;
@@ -377,7 +453,11 @@ function isReliableActionBarCandidate(postElement, candidate) {
return false; return false;
} }
if (hasCommentComposerSignal(candidate)) { if (
hasCommentComposerSignal(candidate)
|| hasCommentContentSignal(candidate)
|| hasCommentSortSignal(candidate)
) {
return false; return false;
} }
@@ -436,18 +516,20 @@ function resolveActionButtonBar(postElement, buttonBar) {
return null; return null;
} }
const dataRoleResolved = findActionBarByDataRoles(postElement); const candidates = [
if (dataRoleResolved) { findActionBarByDataRoles(postElement),
return dataRoleResolved; buttonBar,
} findButtonBar(postElement)
];
if (buttonBar && isReliableActionBarCandidate(postElement, buttonBar)) { for (const candidate of candidates) {
return buttonBar; if (!candidate) {
} continue;
}
const resolved = findButtonBar(postElement); const normalized = normalizeActionBarCandidate(postElement, candidate);
if (resolved && isReliableActionBarCandidate(postElement, resolved)) { if (normalized) {
return resolved; return normalized;
}
} }
return null; return null;
@@ -661,6 +743,28 @@ function insertMarkerAfterAnchor(postElement, marker, anchor) {
return true; return true;
} }
function getNextNonTrackerSibling(node) {
if (!node) {
return null;
}
let current = node.nextElementSibling;
while (current) {
if (
!current.classList
|| (
!current.classList.contains('fb-tracker-ui')
&& !current.classList.contains('fb-tracker-insertion-anchor')
)
) {
return current;
}
current = current.nextElementSibling;
}
return null;
}
function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum = '?') { function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum = '?') {
if (!postElement || !marker) { if (!postElement || !marker) {
return false; return false;
@@ -668,7 +772,6 @@ function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum
const resolvedButtonBar = resolveActionButtonBar(postElement, buttonBar); const resolvedButtonBar = resolveActionButtonBar(postElement, buttonBar);
const actionAnchor = getDirectActionAnchorInPost(postElement, resolvedButtonBar); const actionAnchor = getDirectActionAnchorInPost(postElement, resolvedButtonBar);
const commentBoundary = findCommentBoundaryAnchorInPost(postElement, actionAnchor);
if (actionAnchor && insertMarkerBeforeAnchor(postElement, marker, actionAnchor)) { if (actionAnchor && insertMarkerBeforeAnchor(postElement, marker, actionAnchor)) {
setTrackerInsertionMarkerLocked(marker, true); setTrackerInsertionMarkerLocked(marker, true);
@@ -682,28 +785,13 @@ function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum
return true; return true;
} }
const commentComposerAnchor = findCommentComposerAnchorInPost(postElement); if (postElement.firstChild) {
if (commentComposerAnchor && insertMarkerBeforeAnchor(postElement, marker, commentComposerAnchor)) { postElement.insertBefore(marker, postElement.firstChild);
setTrackerInsertionMarkerLocked(marker, true); } else {
console.log('[FB Tracker] Post #' + postNum + ' - Marker inserted before comment composer'); postElement.appendChild(marker);
return true;
} }
if (commentBoundary && insertMarkerBeforeAnchor(postElement, marker, commentBoundary)) {
setTrackerInsertionMarkerLocked(marker, true);
console.log('[FB Tracker] Post #' + postNum + ' - Marker inserted before comment boundary');
return true;
}
if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement && !postElement.contains(buttonBar.parentElement.parentElement)) {
buttonBar.parentElement.parentElement.insertBefore(marker, buttonBar.parentElement.nextSibling);
setTrackerInsertionMarkerLocked(marker, false);
return true;
}
postElement.appendChild(marker);
setTrackerInsertionMarkerLocked(marker, false); setTrackerInsertionMarkerLocked(marker, false);
console.log('[FB Tracker] Post #' + postNum + ' - Marker fallback appended to post'); console.log('[FB Tracker] Post #' + postNum + ' - Marker fallback prepended to post');
return true; return true;
} }
@@ -717,8 +805,21 @@ function isTrackerInsertionMarkerPlacementValid(postElement, marker, buttonBar)
} }
const actionAnchor = getDirectActionAnchorInPost(postElement, buttonBar); const actionAnchor = getDirectActionAnchorInPost(postElement, buttonBar);
if (actionAnchor && isNodeAfter(marker, actionAnchor)) { if (actionAnchor) {
return false; if (isNodeAfter(marker, actionAnchor)) {
return false;
}
if (actionAnchor.contains(marker) || marker.contains(actionAnchor)) {
return false;
}
if (marker.parentElement === actionAnchor.parentElement) {
const nextSibling = getNextNonTrackerSibling(marker);
if (nextSibling && nextSibling !== actionAnchor) {
return false;
}
}
} }
const commentBoundary = findCommentBoundaryAnchorInPost(postElement); const commentBoundary = findCommentBoundaryAnchorInPost(postElement);