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;
}
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) {
if (!postElement || !candidate || !candidate.isConnected || !postElement.contains(candidate)) {
return false;
@@ -377,7 +453,11 @@ function isReliableActionBarCandidate(postElement, candidate) {
return false;
}
if (hasCommentComposerSignal(candidate)) {
if (
hasCommentComposerSignal(candidate)
|| hasCommentContentSignal(candidate)
|| hasCommentSortSignal(candidate)
) {
return false;
}
@@ -436,18 +516,20 @@ function resolveActionButtonBar(postElement, buttonBar) {
return null;
}
const dataRoleResolved = findActionBarByDataRoles(postElement);
if (dataRoleResolved) {
return dataRoleResolved;
}
const candidates = [
findActionBarByDataRoles(postElement),
buttonBar,
findButtonBar(postElement)
];
if (buttonBar && isReliableActionBarCandidate(postElement, buttonBar)) {
return buttonBar;
}
const resolved = findButtonBar(postElement);
if (resolved && isReliableActionBarCandidate(postElement, resolved)) {
return resolved;
for (const candidate of candidates) {
if (!candidate) {
continue;
}
const normalized = normalizeActionBarCandidate(postElement, candidate);
if (normalized) {
return normalized;
}
}
return null;
@@ -661,6 +743,28 @@ function insertMarkerAfterAnchor(postElement, marker, anchor) {
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 = '?') {
if (!postElement || !marker) {
return false;
@@ -668,7 +772,6 @@ function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum
const resolvedButtonBar = resolveActionButtonBar(postElement, buttonBar);
const actionAnchor = getDirectActionAnchorInPost(postElement, resolvedButtonBar);
const commentBoundary = findCommentBoundaryAnchorInPost(postElement, actionAnchor);
if (actionAnchor && insertMarkerBeforeAnchor(postElement, marker, actionAnchor)) {
setTrackerInsertionMarkerLocked(marker, true);
@@ -682,28 +785,13 @@ function positionTrackerInsertionMarker(postElement, marker, buttonBar, postNum
return true;
}
const commentComposerAnchor = findCommentComposerAnchorInPost(postElement);
if (commentComposerAnchor && insertMarkerBeforeAnchor(postElement, marker, commentComposerAnchor)) {
setTrackerInsertionMarkerLocked(marker, true);
console.log('[FB Tracker] Post #' + postNum + ' - Marker inserted before comment composer');
return true;
if (postElement.firstChild) {
postElement.insertBefore(marker, postElement.firstChild);
} else {
postElement.appendChild(marker);
}
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);
console.log('[FB Tracker] Post #' + postNum + ' - Marker fallback appended to post');
console.log('[FB Tracker] Post #' + postNum + ' - Marker fallback prepended to post');
return true;
}
@@ -717,8 +805,21 @@ function isTrackerInsertionMarkerPlacementValid(postElement, marker, buttonBar)
}
const actionAnchor = getDirectActionAnchorInPost(postElement, buttonBar);
if (actionAnchor && isNodeAfter(marker, actionAnchor)) {
return false;
if (actionAnchor) {
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);