Content-Removal Bookmarklet

Drag the following link into your bookmarks bar: Content Remover

The bookmarklet has been tested and was successful on the following sites.

The source code is shown below.

const insertCSS = (rawCSS) => {
    const css = document.createElement("style");
    css.textContent = rawCSS;
    document.head.appendChild(css);
};

insertCSS(getCSS());

const buttonBar = document.createElement("div");
buttonBar.innerHTML = `
<details id="bookmarklet-help">
    <summary>Help</summary>
    <div>
        Click on items to remove them from the page.
        If "Multi-select" is checked, you can click multiple items to select them and
        then remove them by clicking the "Remove Selected" button.
        Clicking a selected item will unselect it.
        Multi-select also allows you to click and drag your mouse to quickly select items in bulk.
    </div>
</details>
<button id="bookmarklet-undoButton" disabled>Undo</button>
<button id="bookmarklet-endButton">End</button>
<label id="bookmarklet-multiSelect">
    <input type="checkbox" />
    Multi-select
</label>
`;
buttonBar.setAttribute("id", "bookmarklet-buttonBar");
document.body.before(buttonBar);
const endButton = document.querySelector("#bookmarklet-endButton");
const undoButton = document.querySelector("#bookmarklet-undoButton");
const multiSelectButton = document.querySelector(
    "#bookmarklet-multiSelect"
);
const deleteSelectionButton = document.createElement("button");

deleteSelectionButton.textContent = "Remove selected";
deleteSelectionButton.setAttribute("id", "bookmarklet-deleteSelection");
deleteSelectionButton.setAttribute("disabled", "");

/**
    * Handler that prevents mouse changes when hovering over elements.
    *
    * @param {Event} e
    */
function inertHover(e) {
    if (!buttonBar.contains(e.target)) {
        e.target.classList.add("bookmarklet-defaultCursor");
        function cleanUp() {
            e.target.classList.remove("bookmarklet-defaultCursor");
            e.target.removeEventListener("mouseout", cleanUp);
        }
        e.target.addEventListener("mouseout", cleanUp);
    }
}
document.addEventListener("mouseover", inertHover);

/////////////////////////////
// ELEMENT REMOVAL HELPERS //
/////////////////////////////

let currTarget; // holds current hovered element, to be removed on click
let multiSelectMode = false;
/**
    * Given an element `element` to be removed from the DOM, returns an object
    * recording the data needed to restore `element` to its original position:
    * a placeholder (either its previous sibling or its parent if it has
    * no previous sibling) as well as the placeholder's relationship to `element`.
    *
    * @param {Element} element
    * @returns {{placeHolder: ChildNode | Element, relationship: "sibling" | "parent"}}
    */
function savePosition(element) {
    const prevSibling = element.previousSibling;
    if (prevSibling !== null) {
        return { placeHolder: prevSibling, relationship: "sibling" };
    } else
        return {
            placeHolder: element.parentElement,
            relationship: "parent",
        };
}

// Holds deletion history.
const undoQueue = [];

// Custom methods to enable type hinting

/**
    * Pushes an element and its position information to the undo queue.
    *
    * @param {Array<[Element, {placeHolder: ChildNode, relationship: "sibling" | "parent"}]>} entries
    */
function queue(...entries) {
    if (entries.length === 0) return;
    undoQueue.push(entries);
    undoButton.removeAttribute("disabled");
}

/**
    * Pops an entry from the undo queue and returns it. Disables the undo button
    * if the undo is the last entry in the queue
    *
    * @returns {Array<[Element, {placeHolder: ChildNode, relationship: "sibling" | "parent"}]>}
    */
function getQueueTop() {
    if (undoQueue.length === 1) undoButton.setAttribute("disabled", "");
    return undoQueue.pop();
}

function handleClick(e) {
    e.preventDefault();
    e.stopPropagation();
    if (multiSelectMode) handleSelectClick(e.target);
    else removeElement();
}
function removeTarget() {
    if (!multiSelectMode)
        currTarget.classList.remove("bookmarklet-selected");
    currTarget.removeEventListener("click", handleClick);
    currTarget.removeEventListener("mouseout", removeTarget);
}
function setTarget(e) {
    if (buttonBar.contains(e.target)) return;
    currTarget = e.target;
    if (!multiSelectMode) currTarget.classList.add("bookmarklet-selected");
    currTarget.addEventListener("mouseout", removeTarget);
    currTarget.addEventListener("click", handleClick);
}
document.addEventListener("mouseover", setTarget);

//disable highlighting/removal effect on the bookmarklet controls themselves
for (const event of ["mouseover", "click"]) {
    buttonBar.addEventListener(event, (e) => {
        e.stopPropagation();
    });
}

/////////////////////
// CLICK-TO-REMOVE //
/////////////////////

function removeElement() {
    const currTargetCopy = currTarget;
    queue([currTargetCopy, savePosition(currTarget)]);
    currTarget.remove();
    removeTarget();
}

//////////////////////////
// MULTI-SELECTION MODE //
//////////////////////////

const multiTargets = new Set();
let mouseDown = false;
function setMouseDown() {
    mouseDown = true;
}
function setMouseUp() {
    mouseDown = false;
}
function handleSelectMode(e) {
    if (!multiSelectMode || !mouseDown) return;
    e.preventDefault();
    if (!multiTargets.has(e.target)) {
        multiTargets.add(e.target);
        e.target.classList.add("bookmarklet-selected");
        deleteSelectionButton.removeAttribute("disabled");
    }
}
function handleSelectClick(element) {
    if (!multiSelectMode) return;
    if (multiTargets.delete(element)) {
        element.classList.remove("bookmarklet-selected");
        if (multiTargets.size === 0)
            deleteSelectionButton.setAttribute("disabled", "");
    } else {
        multiTargets.add(element);
        element.classList.add("bookmarklet-selected");
        deleteSelectionButton.removeAttribute("disabled");
    }
}
function handleSelectMove(e) {
    if (!multiSelectMode || !mouseDown) return;
    window.getSelection().removeAllRanges();
}
document.addEventListener("mouseover", handleSelectMode);
document.addEventListener("mousemove", handleSelectMove);
document.addEventListener("mousedown", setMouseDown);
document.addEventListener("mouseup", setMouseUp);

/////////////////////
// BUTTON BEHAVIOR //
/////////////////////

undoButton.addEventListener("click", (e) => {
    e.stopPropagation();
    const batch = getQueueTop();
    for (const [element, position] of batch) {
        if (position.relationship === "sibling") {
            position.placeHolder.after(element);
        } else position.placeHolder.prepend(element);
    }
});
endButton.addEventListener("click", (e) => {
    buttonBar.remove();
    if (multiSelectMode) {
        multiSelectButton.dispatchEvent(new Event("input"));
    }
    document.removeEventListener("mouseover", inertHover);
    document.removeEventListener("mouseover", setTarget);
    document.removeEventListener("mousedown", setMouseDown);
    document.removeEventListener("mouseup", setMouseUp);
    document.removeEventListener("mousemove", handleSelectMove);
    document.removeEventListener("mouseover", handleSelectMode);
    e.stopPropagation();
    window.bookmarkletActive = false;
});
multiSelectButton.addEventListener("input", (e) => {
    multiSelectMode = !multiSelectMode;
    if (multiSelectMode) multiSelectButton.append(deleteSelectionButton);
    else {
        for (const element of multiTargets) {
            element.classList.remove("bookmarklet-selected");
        }
        multiTargets.clear();
        deleteSelectionButton.setAttribute("disabled", "");
        deleteSelectionButton.remove();
    }
});
deleteSelectionButton.addEventListener("click", (e) => {
    e.stopPropagation();
    const deleted = [];
    for (const element of multiTargets) {
        if (document.contains(element)) {
            deleted.push([element, savePosition(element)]);
            element.remove();
        }
        element.classList.remove("bookmarklet-selected");
    }
    multiTargets.clear();
    deleteSelectionButton.setAttribute("disabled", "");
    deleted.reverse();
    queue(...deleted);
});

/////////
// CSS //
/////////

function getCSS() {
    return `
    #bookmarklet-buttonBar {
        background-color: white;
        position: relative;
        z-index: 100;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 1em;
        padding: 10px;  
        border-bottom: 1px solid gray;
        font-size: 1.5em;
        font-family: Calibri, sans-serif;
    }
    #bookmarklet-buttonBar * {
        font-family: Calibri, sans-serif;
        color: black;
    }
    .bookmarklet-selected {
        outline: 5px solid black;
        backdrop-filter: brightness(0.8);
    }
    .bookmarklet-defaultCursor {
        cursor: default;
    }
    #bookmarklet-buttonBar button {
        font-size: 0.7em;
        background-color: transparent;
        padding: 0.2em 0.8em;
        border-radius: 4px;
    }
    #bookmarklet-buttonBar button[disabled] {
        color: rgba(0 0 0 / 0.5);
    }
    #bookmarklet-buttonBar button:not([disabled]):hover{
        cursor: pointer;
    }
    #bookmarklet-buttonBar label {
        display: flex;
        align-items: center;
        gap: 0.3em;
        font: inherit;
    }
    #bookmarklet-help {
        position: relative;
    }
    #bookmarklet-help:hover {
        cursor: pointer;
    }
    #bookmarklet-help div {
        font-size: 0.8em;
        width: 20em;
        position: absolute;
        top: 2em;
        padding: 0.2em 0.4em;
        z-index: 1;
        background-color: white;
        box-shadow: 3px 3px 4px 2px rgba(0 0 0 / 50%);
        border-radius: 4px;
    }
    #bookmarklet-multiSelect {
        position: relative;
    }
    #bookmarklet-deleteSelection {
        position: absolute;
        left: 10em;
    }`;
}