Sudoku Exchange Logo

Creating bookmarklets

A bookmarklet in a short piece of Javascript code that you store in your bookmarks menu. When you select the bookmarklet from the menu, the Javascript code is run in the context of the page you have open in your browser.

This page describes how you might write your own bookmarklet that extracts a string of 81 digits from the page and then opens the Sudoku Exchange solver app with those digits loaded in the grid.

Starting simple

The tricky part is building up the string of 81 digits, so for now, we’ll assume you’ve already done that. Here’s some Javascript code that has a digit string in a variable and loads it on SudokuExchange.com:

s = '000001230123008040804007650765000000000000000000000123012300804080400765076500000';
location.href = 'https://sudokuexchange.com/play/?s='+s;

To turn that into a bookmarklet, we need to:

Which gives us this single long line that we can bookmark: Load a hard-coded digit string

javascript:s='000001230123008040804007650765000000000000000000000123012300804080400765076500000';location.href='https://sudokuexchange.com/play/?s='+s;

Opening a new tab

One problem with our starting example is that it replaces the page you’re viewing with the Sudoku Exchange app. If, instead, we want the app to open in a new tab, we could replace the second line:

s = '000001230123008040804007650765000000000000000000000123012300804080400765076500000';
window.open('https://sudokuexchange.com/play/?s='+s, '_blank');

While that does successfully open the app in a new window, unfortunately it also replaces the page we were viewing with the value returned by the last statement. In our code the last statement returns a Window object, so the page we were viewing get’s replaced with this short piece of text: [object Window]. To make matters worse, you can’t even use the back button to re-display the page.

To avoid that unfortunate side-effect, we need to ensure that there is no value returned by the last statement. One way to achieve that is by wrapping the statement with void( ... ):

s = '000001230123008040804007650765000000000000000000000123012300804080400765076500000';
void(
  window.open('https://sudokuexchange.com/play/?s='+s, '_blank')
);

Once again we can reformat our code as a bookmarklet: Load a hard-coded digit string in a new window

javascript:s='000001230123008040804007650765000000000000000000000123012300804080400765076500000';void(window.open('https://sudokuexchange.com/play/?s='+s, '_blank'));

Grabbing selected text

Instead of hard-coding the digit string, a more useful bookmarklet might use the contents of any selected text in the page. The user could use their mouse to highlight a string of 81 digits in the page and then select the bookmarklet from their menu. This code uses the Window.getSelection() method to grab the current selection object and then the Selection.toString() method to turn that into a plain text string:

s = window.getSelection().toString();
void(
  window.open('https://sudokuexchange.com/play/?s='+s, '_blank')
);

As a bookmarklet: Load selected text

javascript:s=window.getSelection().toString();void(window.open('https://sudokuexchange.com/play/?s='+s, '_blank'));

Making our code more robust

When the user highlights text with their mouse it is very easy to accidentally include some whitespace characters too. We can use the String.trim() method to remove spaces from the beginning and end of the string.

s = window.getSelection().toString().trim();

If the user doesn’t select enough digits, or if they include some non-digit characters in the selection, it would be nice to catch the error and display a message rather than opening a broken puzzle in a new tab. We can use pattern matching with the String.match() method to check that the selected text contains exactly 81 digits and that they are not all zeros:

s = window.getSelection().toString().trim();
if(s.match(/^[0-9]{81}$/) && s.match(/[1-9]/)) {
  alert('Selected text does not look like a Sudoku puzzle');
}
else {
  void(
    window.open('https://sudokuexchange.com/play/?s='+s, '_blank')
  );
}

Sometimes sudoku puzzle strings are represented with dots (‘.’) instead of zeros (‘0’) to represent blank cells. We can use the String.replace() method to turn each ‘.’ into a ‘0’.

s = window.getSelection().toString().trim().replace(/[.]/g, '0');
if(s.match(/^[0-9]{81}$/) && s.match(/[1-9]/)) {
  alert('Selected text does not look like a Sudoku puzzle');
}
else {
  void(
    window.open('https://sudokuexchange.com/play/?s='+s, '_blank')
  );
}

To make our code a bit shorter, we can replace the if ... else block with a ternary operator:

s = window.getSelection().toString().trim().replace(/[.]/g, '0');
void(
  (s.match(/^[0-9]{81}$/) && s.match(/[1-9]/))
  ? window.open('https://sudokuexchange.com/play/?s='+s, '_blank')
  : alert('Selected text does not look like a Sudoku puzzle')
);

As a bookmarklet: Load 81 digit string into SudokuExchange.com

javascript:s=window.getSelection().toString().trim().replace(/[.]/g, '0');void((s.match(/^[0-9]{81}$/)&&s.match(/[1-9]/))?window.open('https://sudokuexchange.com/play/?s='+s,'_blank'):alert('Selected text does not look like a Sudoku puzzle'));

If you save this final version to to your bookmarks menu, you can try it out on the Patterns Game Results page.

Scraping Sudoku puzzles

In order to scrape a page on a puzzle site, you’ll need to explore the structure of the page using browser tools like the DOM (document object model) inspector. Then you might use a DOM method like Document.querySelectorAll() to loop through all elements with a particular class, or all children of an element with a particular "id".

As you loop through each element, you would replace blank cells with ‘0’ and cells containing a given digit with ‘1’ through ‘9’. The value for the given digit might come from the element’s text content, or an attribute, or even the filename in the src attribute of an <image> element.

Refer to the provided bookmarklets for specific code examples. From this point it’s over to you, your creativity and your problem solving skills.