Advanced Bookmarklet Techniques
Push the boundaries of what's possible with sophisticated bookmarklet programming patterns.
Beyond the Basics
Once you've mastered basic bookmarklets, it's time to explore advanced techniques that can turn simple scripts into powerful tools. This guide covers sophisticated patterns, performance optimizations, and creative solutions to common limitations.
🚀 Advanced Topics
- • Async/await patterns in bookmarklets
- • Cross-frame communication
- • Dynamic script injection
- • State persistence techniques
- • Advanced DOM manipulation
Asynchronous Patterns
⏳ Promise-Based Bookmarklets
Handle asynchronous operations elegantly
javascript:(async function() {
try {
// Show loading indicator
var loader = document.createElement('div');
loader.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);' +
'background:#000;color:#fff;padding:20px;border-radius:10px;z-index:9999';
loader.textContent = 'Loading...';
document.body.appendChild(loader);
// Fetch data asynchronously
var response = await fetch('https://api.example.com/data');
var data = await response.json();
// Process results
loader.textContent = 'Processing ' + data.length + ' items...';
await new Promise(resolve => setTimeout(resolve, 1000));
// Update UI
console.log('Data processed:', data);
loader.remove();
} catch(error) {
alert('Error: ' + error.message);
}
})();
🔄 Sequential Operations
Chain multiple async operations with error handling
javascript:(async function() {
var tasks = [
() => fetch('/api/user').then(r => r.json()),
() => fetch('/api/posts').then(r => r.json()),
() => fetch('/api/comments').then(r => r.json())
];
var results = [];
var progress = document.createElement('progress');
progress.style.cssText = 'position:fixed;top:0;left:0;width:100%;z-index:9999';
progress.max = tasks.length;
document.body.appendChild(progress);
for(let i = 0; i < tasks.length; i++) {
try {
results[i] = await tasks[i]();
progress.value = i + 1;
} catch(e) {
results[i] = {error: e.message};
}
}
progress.remove();
console.log('All tasks complete:', results);
})();
⚡ Parallel Execution
Run multiple operations simultaneously
javascript:(async function() {
var urls = Array.from(document.querySelectorAll('a'))
.map(a => a.href)
.filter(url => url.startsWith('http'));
console.log('Checking ' + urls.length + ' links...');
var results = await Promise.allSettled(
urls.map(url =>
fetch(url, {method: 'HEAD', mode: 'no-cors'})
.then(() => ({url, status: 'ok'}))
.catch(() => ({url, status: 'failed'}))
)
);
var failed = results.filter(r => r.value.status === 'failed');
console.log('Found ' + failed.length + ' broken links');
})();
Dynamic Script Injection
📦 Loading External Libraries
Inject and use external JavaScript libraries
javascript:(function() {
function loadScript(src) {
return new Promise((resolve, reject) => {
var script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
loadScript('https://cdn.jsdelivr.net/npm/chart.js')
.then(() => {
// Use the loaded library
var canvas = document.createElement('canvas');
canvas.style.cssText = 'position:fixed;top:10px;right:10px;width:300px;height:200px;z-index:9999';
document.body.appendChild(canvas);
new Chart(canvas, {
type: 'bar',
data: {
labels: ['Images', 'Scripts', 'Styles'],
datasets: [{
data: [
document.images.length,
document.scripts.length,
document.styleSheets.length
]
}]
}
});
})
.catch(err => alert('Failed to load library: ' + err));
})();
🧩 Modular Bookmarklet System
Create a bookmarklet that loads modules on demand
javascript:(function() {
window.BookmarkletModules = window.BookmarkletModules || {};
var moduleLoader = {
load: function(moduleName) {
if(this.modules[moduleName]) {
return this.modules[moduleName]();
}
throw new Error('Module not found: ' + moduleName);
},
modules: {
dom: () => ({
highlight: selector => {
document.querySelectorAll(selector).forEach(el => {
el.style.outline = '2px solid red';
});
},
remove: selector => {
document.querySelectorAll(selector).forEach(el => el.remove());
}
}),
analytics: () => ({
pageStats: () => ({
words: document.body.innerText.split(/\s+/).length,
images: document.images.length,
links: document.links.length,
loadTime: performance.timing.loadEventEnd - performance.timing.navigationStart
})
})
}
};
// Usage
var dom = moduleLoader.load('dom');
dom.highlight('img');
var stats = moduleLoader.load('analytics').pageStats();
console.table(stats);
})();
State Persistence
💾 LocalStorage State Management
Persist data between bookmarklet executions
javascript:(function() {
var STORAGE_KEY = 'bookmarklet_state_' + location.hostname;
var state = {
load: function() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
} catch(e) {
return {};
}
},
save: function(data) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
},
update: function(key, value) {
var data = this.load();
data[key] = value;
this.save(data);
return data;
}
};
// Track page visits
var visits = state.load().visits || 0;
state.update('visits', visits + 1);
state.update('lastVisit', new Date().toISOString());
// Show stats
var data = state.load();
alert('Visits: ' + data.visits + '\nLast: ' + data.lastVisit);
})();
🔄 Cross-Tab Communication
Sync bookmarklet state across browser tabs
javascript:(function() {
var channel = new BroadcastChannel('bookmarklet_sync');
// Listen for messages from other tabs
channel.onmessage = function(e) {
console.log('Message from another tab:', e.data);
if(e.data.action === 'highlight') {
document.body.style.backgroundColor = e.data.color;
}
};
// Send message to all tabs
var color = prompt('Enter a color to sync across all tabs:');
if(color) {
channel.postMessage({
action: 'highlight',
color: color,
sender: location.href
});
// Apply to current tab too
document.body.style.backgroundColor = color;
}
})();
Advanced DOM Manipulation
🎭 Virtual DOM Diffing
Efficiently update DOM with minimal reflows
javascript:(function() {
var vdom = {
create: function(tag, props, ...children) {
return {tag, props, children: children.flat()};
},
render: function(vnode) {
if(typeof vnode === 'string') return document.createTextNode(vnode);
var el = document.createElement(vnode.tag);
for(var prop in vnode.props) {
if(prop === 'style') {
Object.assign(el.style, vnode.props[prop]);
} else if(prop.startsWith('on')) {
el[prop] = vnode.props[prop];
} else {
el.setAttribute(prop, vnode.props[prop]);
}
}
vnode.children.forEach(child => {
el.appendChild(this.render(child));
});
return el;
}
};
// Create UI using virtual DOM
var ui = vdom.create('div', {
style: {
position: 'fixed',
top: '10px',
right: '10px',
background: '#000',
color: '#fff',
padding: '10px',
borderRadius: '5px',
zIndex: 9999
}
},
vdom.create('h3', {}, 'Page Stats'),
vdom.create('p', {}, 'Words: ' + document.body.innerText.split(/\s+/).length),
vdom.create('button', {
onclick: function() { this.parentElement.remove(); }
}, 'Close')
);
document.body.appendChild(vdom.render(ui));
})();
🎯 MutationObserver Patterns
React to dynamic page changes
javascript:(function() {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// Auto-remove ads as they appear
mutation.addedNodes.forEach(function(node) {
if(node.nodeType === 1) { // Element node
if(node.classList.contains('ad') ||
node.id.includes('ad') ||
node.querySelector('[class*="ad"]')) {
node.style.display = 'none';
console.log('Hidden ad element:', node);
}
}
});
});
});
// Start observing
observer.observe(document.body, {
childList: true,
subtree: true
});
// Stop after 30 seconds to prevent memory leaks
setTimeout(() => {
observer.disconnect();
console.log('Ad blocker stopped');
}, 30000);
console.log('Ad blocker active for 30 seconds');
})();
Cross-Frame Communication
🖼️ IFrame Manipulation
Interact with content inside iframes
javascript:(function() {
function processFrames(win, depth = 0) {
var indent = ' '.repeat(depth);
console.log(indent + 'Frame:', win.location.href);
// Try to access frame content (same-origin only)
try {
var links = win.document.querySelectorAll('a');
console.log(indent + ' Links:', links.length);
// Process nested frames
Array.from(win.frames).forEach(frame => {
processFrames(frame, depth + 1);
});
} catch(e) {
console.log(indent + ' Access denied (cross-origin)');
}
}
// Start from top window
processFrames(window);
// Count all accessible frames
var frameCount = 0;
function countFrames(win) {
frameCount++;
try {
Array.from(win.frames).forEach(countFrames);
} catch(e) {}
}
countFrames(window);
alert('Total frames: ' + frameCount);
})();
📨 PostMessage Communication
Safely communicate across origins
javascript:(function() {
// Listen for responses
window.addEventListener('message', function handler(e) {
if(e.data.type === 'bookmarklet_response') {
console.log('Response from', e.origin, ':', e.data);
window.removeEventListener('message', handler);
}
});
// Send message to all frames
function sendToAllFrames(win, message) {
// Send to current window
win.postMessage(message, '*');
// Send to all child frames
for(var i = 0; i < win.frames.length; i++) {
try {
win.frames[i].postMessage(message, '*');
sendToAllFrames(win.frames[i], message);
} catch(e) {
// Cross-origin frame, use postMessage only
win.frames[i].postMessage(message, '*');
}
}
}
sendToAllFrames(window, {
type: 'bookmarklet_ping',
timestamp: Date.now()
});
})();
Performance Optimization
⚡ Debouncing and Throttling
Optimize event handlers for better performance
javascript:(function() {
function debounce(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
function throttle(func, limit) {
var inThrottle;
return function() {
var context = this, args = arguments;
if(!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Highlight elements on hover (throttled)
var highlight = throttle(function(e) {
document.querySelectorAll('.highlighted').forEach(el => {
el.classList.remove('highlighted');
el.style.outline = '';
});
e.target.classList.add('highlighted');
e.target.style.outline = '2px solid red';
}, 100);
document.addEventListener('mouseover', highlight);
// Clean up after 10 seconds
setTimeout(() => {
document.removeEventListener('mouseover', highlight);
document.querySelectorAll('.highlighted').forEach(el => {
el.classList.remove('highlighted');
el.style.outline = '';
});
}, 10000);
})();
🚀 Web Worker Integration
Offload heavy computations to background threads
javascript:(function() {
// Create worker from inline code
var workerCode = `
self.onmessage = function(e) {
var text = e.data;
var words = text.match(/\\b\\w+\\b/g) || [];
var frequency = {};
words.forEach(word => {
word = word.toLowerCase();
frequency[word] = (frequency[word] || 0) + 1;
});
var sorted = Object.entries(frequency)
.sort((a, b) => b[1] - a[1])
.slice(0, 20);
self.postMessage(sorted);
};
`;
var blob = new Blob([workerCode], {type: 'application/javascript'});
var worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = function(e) {
console.log('Top 20 words:', e.data);
var list = e.data.map(([word, count]) => word + ': ' + count).join('\n');
alert('Word Frequency Analysis:\n\n' + list);
worker.terminate();
};
// Send page text to worker
worker.postMessage(document.body.innerText);
})();
Creative Applications
🎨 Advanced Use Cases
- • Create mini-games that run on any webpage
- • Build collaborative annotation tools
- • Implement custom analytics tracking
- • Create page-specific automation workflows
- • Build developer tools for specific frameworks
- • Implement custom accessibility features
Best Practices for Advanced Bookmarklets
✅ Do's
- • Use proper error handling
- • Clean up event listeners
- • Implement timeouts for long operations
- • Test across different page types
- • Document complex logic
- • Use feature detection
❌ Don'ts
- • Don't create memory leaks
- • Avoid infinite loops
- • Don't rely on external CDNs
- • Don't modify critical page functions
- • Avoid blocking the main thread
- • Don't store sensitive data
Push Your Limits
These advanced techniques open up endless possibilities. Experiment, combine different patterns, and create bookmarklets that solve real problems in innovative ways.