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;
}`;
}