[Userscript] Wanikani Review Answer History

Wanikani Review Answer History

Have you ever wanted to see your past review answers? Do you want to reminisce on all those times you typed gibberish for the answer because you didn’t know it? Well now you can!

What does it do?

This script records your answers during reviews and displays them on each item’s page.

The purpose of this script is to provide helpful information on leeches such as what SRS levels the item typically gets to before you get it wrong, and also the kind of items you confuse the leech with. The SRS level displayed is the level that you reviewed the item at.

The script should also work with both the double check script and the (obsolete) override script if you are using either of those:


As said above, this script records your review answers since Wanikani doesn’t. Because of this, the only data that will be available to you will be data recorded from reviews done with this script installed. If you do reviews on multiple devices or you switch browsers, your data will not be transferred across. I wish I could change that, but that’s just the way the cookie caches.


This script requires rfindley’s Wanikani Open Framework if you don’t already have it installed.

This script can be installed here:

Thanks to @Kumirei and @Rrwrex for helping.


Oooh, this could be useful going through my burns. Ty for this!

1 Like

Super cool!

1 Like

I had to disable my Review Queue Limiter script [1] and the Nippongrammar Extension script [2], but it seems to work now. However, after noticing the problem with the Nippongrammar Extension script, I’m wondering if your script would have trouble with image-based radical items like viking?

  1. The problem is that this script adds an input box before the review answer input box. Maybe it would be better to increase the specificity of your selector? You could use the user-response id. ↩︎

  2. Here, the problem was that this script replaces the displayed item with a stroke order animation, so your itemElem.innerText results in an empty string in this case. Maybe it would have been simpler to identify the items in WKAnswerHistory not by the type/slug combination but by their id number? ↩︎


Yes! In fact, it actually had a problem with all radicals, but I didn’t realize because I never get radical reviews to test on and I just assumed it was okay for them too. Turns out nope, they work differently. Both those extensions and the radicals should be fixed now, however I can’t test out the radicals so if someone could confirm it for me, it would be appreciated!

1 Like

No, the radicals still don’t work. I went through your code with step-by-step debugging and there are still a few problems:

  • In the case of a radical review, let itemType = itemElem.classList[0] is radical, but WKAnswerHistory does not contain a property radical but only a property radicals, so the line if (!WKAnswerHistory[itemType][item]){ throws an exception.

  • I have now tested your script on a different PC where I have Reorder Omega installed, and item = itemElem.innerText now results in


    because Reorder Omega adds a preset dropdown to the interface. Furthermore, if the item is a radical, you return the entire item object in getItemSRS("item") and then try to use it as if it were a string containing the slug in the line if (!WKAnswerHistory[itemType][item]){ (item is not a string containing the slug at this point!).

    Instead of trying to read the slug from the DOM which is more likely modified by other scripts, it would probably be better to always read it from the wkof item with items_by_id[review_item.id].data.slug.

  • Regarding slugs: does anyone know if they are now guaranteed to stay the same for every WK item? In the past, some radicals were renamed (for example butcher => building, and its slug is now building). Unless they never again do a radical overhaul, using the type/slug pair for identifying items might break at one point.

Alternatively, all these problems could be avoided by using the unique item id instead of the type/slug pair for identifying items: During reviews, the id is directly available in $.jStorage.get('currentItem').id, and on the item pages, you can get the id with parseInt(document.querySelector('meta[name=subject_id]').content).


I know this is for answer history, but it might be fun to add some of the known dates for items even if you don’t have an answer recorded. You could get the date of the lesson, the date of reaching guru, and the burn date of all items

1 Like

So a big reason why I wrote the script the way I did was because I’ve never used the API before, and originally, I had intended to make the script without WKOF too. Over time as the complexity grew and I found that I needed to use the API, I never changed those things. And yes, missing that ‘s’ was dumb. The script now should convert the object to use “radical” instead.

All input-related issues should be fixed, I was grabbing the input field using querySelector("input") which was incredibly stupid in retrospect. There’s literally an id on the element that I ignored.

going to pretend I didn’t just blindly assume I knew what the API was returning when I taped that solution onto getItemSRS. That should now be fixed too.

So the main lesson I’m getting from this is that I should really be trying to learn the API some more, which is fine since this is the first time I’ve tried to do anything with it. Scraping the data from the DOM is something I never wanted to do with anything more than a simple styling script, but it was kind of a necessity for me here because I really didn’t know what I was doing with the API :sweat_smile: .

I can’t do much else tonight so hopefully the radical issue should be fixed (although the image radicals will unfortunately have to remain broken for the meantime). But thank you for checking my code and your insight, it has been a huge help! I will hopefully get around to changing everything to item ids at some point over the next few days.


That’s a good idea! I wanted to add level ups/downs in some way anyway so I may try doing that tomorrow or whenever I get time.

1 Like

Update: This script now includes milestones for each item

Other changes:

  • Items are now recorded via the item id so all radicals (including image radicals) should work properly.
  • Dates now display in a more user-friendly format.
  • Added a failsafe when converting user data (I’ve had a bug wipe my data during testing so in the extremely unlikely event that happens again, it can be stopped).

Known issues:

  • On chrome I’ve found that the milestone data uses UTC time instead of local time. I have no idea what’s up with this, and it works fine on edge. Not a huge issue, just annoying.
  • Milestones currently don’t display for items with no data. I just forgot to add it. I’ll get round to it when I next have time.
  • Maybe more issues that I haven’t found because I so rarely get radical reviews to test.

Future plans:

  • Adding indications for which reviews have triggered a level up/level down (I don’t know how feasible this is).
  • Adding data on the proportions that each item’s synonym has been answered with. For example, if you have an item like 広げる, it will break down what percentage of your reviews you’ve answered with “to spread something” vs “to unfold something” vs “to open something up” vs user synonyms. And then the same for multiple accepted kanji readings.

Tagging @Kumirei since they suggested the milestones in the first place.


There are probably many ways to do this, but my first idea would be to fetch all the unpassed kanji on the current level when the reviews load, then mark them as passed when they level up to guru, and check every time if more than 90% are gurued.

The simpler way would be to just hit the API every time an apprentice 4 item is answered correctly

General question: How does this script handle undoing answers?

1 Like

I was originally thinking of trying to use the recorded data somehow in order to save storing more information but I don’t think that’d work so I’ll probably have to do one of those two ways.

I’m using a mutation observer to record when answers are made and whether the answer is correct or incorrect (just tracking class name changes on the input’s parent), and it only creates a new record if it goes from nothing to something. Otherwise, it considers it an override and updates just the status of the most recent item.

I remember testing it with the double check script although I use the override script usually and I can’t remember whether you can continuously toggle ignore/unignore answer. If you can, then maybe it will work, but it may just create a new record which would be an issue. I’ll have to look at it and test it out later.

1 Like

FYI this is broken on item pages due to WK changes

1 Like

I’m at work right now so I’ll have to fix it later

1 Like