Bookmarklet Compatibility Guide
Ensure your bookmarklets work seamlessly across all browsers, devices, and websites.
Cross-Browser Compatibility
While bookmarklets are generally portable, differences in browser implementations, security policies, and JavaScript engines can cause compatibility issues. This guide helps you write bookmarklets that work everywhere.
๐ฏ Compatibility Goals
- โข Work in all major browsers
- โข Support mobile devices
- โข Handle different JavaScript versions
- โข Respect security restrictions
- โข Gracefully degrade when features unavailable
Browser Support Matrix
Feature | Chrome | Firefox | Safari | Edge | Mobile |
---|---|---|---|---|---|
Basic JavaScript | โ | โ | โ | โ | โ |
ES6+ Features | โ | โ | โ | โ | Varies |
Async/Await | โ | โ | โ | โ | Newer |
localStorage | โ | โ | โ | โ | Limited |
Cross-Origin Requests | CORS | CORS | CORS | CORS | CORS |
Clipboard API | โ | โ | Partial | โ | โ |
Web Workers | โ | โ | โ | โ | Limited |
URL Length Limit | 2MB | 64KB | 80KB | 2MB | 2-8KB |
JavaScript Version Compatibility
ES5 Compatible Version
Maximum compatibility for older browsers
javascript:(function() {
// Use var instead of let/const
var elements = document.getElementsByTagName('*');
// Use for loop instead of forEach
for(var i = 0; i < elements.length; i++) {
var el = elements[i];
// Use indexOf instead of includes
if(el.className && el.className.indexOf('highlight') > -1) {
el.style.backgroundColor = 'yellow';
}
}
// Use function instead of arrow functions
setTimeout(function() {
alert('Highlighted ' + document.querySelectorAll('.highlight').length + ' elements');
}, 100);
})();
Modern ES6+ Version
Cleaner code for modern browsers
javascript:(() => {
// Modern syntax with arrow functions
const elements = [...document.querySelectorAll('*')];
// Array methods and template literals
const highlighted = elements.filter(el =>
el.classList?.contains('highlight')
);
highlighted.forEach(el => {
el.style.backgroundColor = 'yellow';
});
// Template literals
setTimeout(() => {
alert(`Highlighted ${highlighted.length} elements`);
}, 100);
})();
Feature Detection Pattern
Gracefully handle missing features
javascript:(function() {
// Check for modern features
var hasPromises = typeof Promise !== 'undefined';
var hasArrows = (function() {
try {
eval('()=>{}');
return true;
} catch(e) {
return false;
}
})();
// Use appropriate syntax
if(hasPromises && hasArrows) {
// Modern code path
fetch('/api/data')
.then(r => r.json())
.then(data => console.log(data));
} else {
// Legacy fallback
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onload = function() {
console.log(JSON.parse(xhr.responseText));
};
xhr.send();
}
})();
Mobile Device Compatibility
๐ฑ Mobile Limitations
- โข Limited URL length (2-8KB)
- โข No drag-and-drop bookmark creation
- โข Touch events instead of mouse events
- โข Different viewport handling
- โข Restricted clipboard access
- โข No keyboard shortcuts
Mobile-Friendly Bookmarklet
Optimized for touch devices
javascript:(function() {
// Check if mobile device
var isMobile = /Android|webOS|iPhone|iPad|iPod/i.test(navigator.userAgent);
// Create mobile-friendly UI
var panel = document.createElement('div');
panel.style.cssText =
'position:fixed;' +
'bottom:0;left:0;right:0;' +
'background:#000;color:#fff;' +
'padding:' + (isMobile ? '20px' : '10px') + ';' +
'font-size:' + (isMobile ? '16px' : '14px') + ';' +
'z-index:9999;' +
'touch-action:none;';
// Add touch-friendly buttons
var btnStyle =
'background:#007AFF;color:#fff;' +
'border:none;border-radius:5px;' +
'padding:' + (isMobile ? '15px 20px' : '8px 15px') + ';' +
'margin:5px;font-size:inherit;' +
'cursor:pointer;';
var closeBtn = document.createElement('button');
closeBtn.style.cssText = btnStyle;
closeBtn.textContent = 'Close';
closeBtn.onclick = function() { panel.remove(); };
panel.innerHTML = '<div>Mobile Bookmarklet Active</div>';
panel.appendChild(closeBtn);
document.body.appendChild(panel);
})();
Installing on Mobile
iOS Safari:
- Copy the bookmarklet code
- Bookmark any page
- Edit the bookmark
- Replace URL with bookmarklet code
- Save and use from bookmarks
Android Chrome:
- Type the bookmarklet name in address bar
- Chrome will auto-suggest the bookmarklet
- Tap to execute
Security and CSP Compatibility
๐ Content Security Policy Issues
Many modern websites use CSP headers that can block bookmarklets. Common restrictions:
- โข
script-src 'self'
- Blocks inline scripts - โข
default-src 'none'
- Blocks all external resources - โข
unsafe-inline
- Required for bookmarklets to work
CSP Detection and Handling
Check if bookmarklet can run
javascript:(function() {
// Test if inline scripts are allowed
try {
// Try to create and execute a script
var testScript = document.createElement('script');
testScript.textContent = 'window.__bookmarkletTest = true;';
document.head.appendChild(testScript);
if(window.__bookmarkletTest) {
delete window.__bookmarkletTest;
testScript.remove();
// Bookmarklet code here
alert('Bookmarklet can run on this site!');
} else {
throw new Error('Script blocked');
}
} catch(e) {
// CSP is blocking execution
console.error('CSP blocking bookmarklet:', e);
// Try alternative approach
var popup = window.open('', 'Bookmarklet', 'width=400,height=300');
if(popup) {
popup.document.write(
'<h3>CSP Restriction</h3>' +
'<p>This site blocks bookmarklets. ' +
'Try using the browser console instead.</p>'
);
}
}
})();
DOM API Compatibility
Legacy DOM Methods
Fallbacks for older browsers
javascript:(function() {
// QuerySelector fallback
function $(selector) {
if(document.querySelector) {
return document.querySelector(selector);
}
// Fallback for basic selectors
if(selector[0] === '#') {
return document.getElementById(selector.slice(1));
}
if(selector[0] === '.') {
var elements = document.getElementsByClassName(selector.slice(1));
return elements[0];
}
return document.getElementsByTagName(selector)[0];
}
// ClassList fallback
function addClass(el, className) {
if(el.classList) {
el.classList.add(className);
} else {
el.className += ' ' + className;
}
}
// forEach fallback
function forEach(nodeList, callback) {
if(nodeList.forEach) {
nodeList.forEach(callback);
} else {
Array.prototype.forEach.call(nodeList, callback);
}
}
// Usage
var header = $('#header') || $('h1');
if(header) {
addClass(header, 'highlighted');
}
})();
Modern API Polyfills
Add missing functionality
javascript:(function() {
// Element.closest() polyfill
if(!Element.prototype.closest) {
Element.prototype.closest = function(selector) {
var el = this;
while(el && el.nodeType === 1) {
if(el.matches(selector)) return el;
el = el.parentNode;
}
return null;
};
}
// String.includes() polyfill
if(!String.prototype.includes) {
String.prototype.includes = function(search, start) {
return this.indexOf(search, start) !== -1;
};
}
// Object.assign() polyfill
if(!Object.assign) {
Object.assign = function(target) {
for(var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for(var key in source) {
if(source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
};
}
})();
Testing for Compatibility
๐งช Compatibility Test Suite
Comprehensive browser feature detection
javascript:(function() {
var tests = {
'JavaScript Version': (function() {
try { eval('let x = 1'); return 'ES6+'; }
catch(e) { return 'ES5'; }
})(),
'DOM Level': document.querySelector ?
(document.querySelectorAll ? 'Level 3' : 'Level 2') : 'Level 1',
'localStorage': typeof localStorage !== 'undefined',
'sessionStorage': typeof sessionStorage !== 'undefined',
'JSON': typeof JSON !== 'undefined',
'Promise': typeof Promise !== 'undefined',
'Fetch API': typeof fetch !== 'undefined',
'Arrow Functions': (function() {
try { eval('()=>{}'); return true; }
catch(e) { return false; }
})(),
'Template Literals': (function() {
try { eval('`test`'); return true; }
catch(e) { return false; }
})(),
'Async/Await': (function() {
try { eval('async function test(){}'); return true; }
catch(e) { return false; }
})(),
'classList': document.body.classList !== undefined,
'Dataset': document.body.dataset !== undefined,
'MutationObserver': typeof MutationObserver !== 'undefined',
'IntersectionObserver': typeof IntersectionObserver !== 'undefined',
'URL Length Limit': (function() {
var len = window.location.href.length;
if(len > 65536) return '>64KB';
if(len > 8192) return '>8KB';
if(len > 2048) return '>2KB';
return 'Unknown';
})()
};
var results = 'Browser Compatibility Report\n\n';
for(var test in tests) {
results += test + ': ' + tests[test] + '\n';
}
// Create report window
var win = window.open('', 'Compatibility', 'width=400,height=600');
win.document.write('<pre>' + results + '</pre>');
})();
Best Practices for Maximum Compatibility
โ Do's
- โข Use feature detection
- โข Provide fallbacks
- โข Test in multiple browsers
- โข Keep code concise
- โข Handle errors gracefully
- โข Use standard DOM APIs
- โข Document browser requirements
โ Don'ts
- โข Don't assume features exist
- โข Avoid browser-specific APIs
- โข Don't use eval() unnecessarily
- โข Don't ignore mobile users
- โข Avoid large external dependencies
- โข Don't break on errors
- โข Don't exceed URL limits
Universal Compatibility
Writing compatible bookmarklets requires careful consideration of browser differences, but the result is tools that work for everyone. Test thoroughly and provide graceful fallbacks.