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.