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

FeatureChromeFirefoxSafariEdgeMobile
Basic JavaScriptโœ“โœ“โœ“โœ“โœ“
ES6+ Featuresโœ“โœ“โœ“โœ“Varies
Async/Awaitโœ“โœ“โœ“โœ“Newer
localStorageโœ“โœ“โœ“โœ“Limited
Cross-Origin RequestsCORSCORSCORSCORSCORS
Clipboard APIโœ“โœ“Partialโœ“โœ—
Web Workersโœ“โœ“โœ“โœ“Limited
URL Length Limit2MB64KB80KB2MB2-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:

  1. Copy the bookmarklet code
  2. Bookmark any page
  3. Edit the bookmark
  4. Replace URL with bookmarklet code
  5. Save and use from bookmarks

Android Chrome:

  1. Type the bookmarklet name in address bar
  2. Chrome will auto-suggest the bookmarklet
  3. 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.