So, as I said above, Web Workers are able to call setTimeout() without the 1 second delay for inactive tabs. My solution then was to create a Web Worker which was only responsible for calling setTimeout() (called in its onmessage event listener). Then, at the end of each game loop I called:
this.worker.postMessage(null)
It could be argued that it would be more efficient to give the Web Worker more responsibility than just calling setTimeout(), since I've already added the overhead of waiting for messages to be sent between the main thread and the worker. This is something I might look at in the future.
The main problem with doing it this way is compatibility with IE; IE did not get support for web workers until version 10.0. This isn't really a concern for me but I think it's worth mentioning.