Parts of Lessons converted to React now in Preview

I probably will need to at some point. I’m also just really busy right now and don’t feel like putting in the time to test the script. Lesson Filter is the one script I made that I want to make sure always works, which is why I asked about that one in particular.

A solution I have used (based on what the lesson spoiler removal script does), which still seems to work reliably with react, is to add a click event listener to document. If you click on the page to switch the lesson tab, it will be fired. For some reason, a click event is also fired if you use the keyboard to switch the lesson tab, so you don’t need to handle that separately. Obviously, click events can happen for reasons other than a lesson tab switch, but you can detect whether a switch actually happened by looking at $("#supplement-nav ul").text().trim() which gives the name of the active reading/meaning/etc. tab. Also, for the event listener to work, you need to make sure to add it using standard javascript rather than jQuery, because jQuery event listeners fire before React event listeners, but standard javascript event listeners will fire after, as long as you wait until document-end to add the standard listener.

To add things to the dom and make sure they get re-added only when removed by react, what I am doing is something like making a function called maintainChild(selector, node) which adds node as a child of the node matched by selector and adds it back if it gets removed for some reason such as if it’s parent clears its children or its parent is removed from the DOM.

  1. Register a MutationObserver on doccment with childlist:true, subtree:true.
  2. Whenever it fires, get the current element from the DOM matching the selector and check that node is a child of it. If not, add it as a child.

I did a quick check and Lessons Filter works in preview. I don’t know whether some issue would show up if a more thorough test is done though

1 Like

source code leak

const Kanji = ({ char }) => {
    return <div>{char}</div>

I’m a bit surprised that hitting enter/left/right generates click events instead of directly calling some function previous() or next(), but I’m even more surprised that they are not click events for the prev-btn or the next-btn, but for the tabs in the navigation bar or the batch list at the bottom, depending on the current state.

Combined with a check if "l/currentLesson" has changed, that would probably work (otherwise it would miss the change in situations like [Currently on “Radicals” tab of a kanji A] => [Click on different kanji B in batch list at the bottom] => [Now on “Radicals” tab of kanji B]).

The advantage of the click event method over the MutationObserver method is, I assume, that the code executes after React has finished its work. Maybe I will experiment a bit with it and see if I like it better (since my last post, I have improved the MutationObserver method a bit, but I’m still not really happy with it).

Thank you for the suggestions!

I have now experimented a bit with exploiting the click events, and it works in most cases, but when I’m clicking #prev-btn or #next-btn to go from one item to a different item, there are two click events (first on #batch-items li, then on #prev-btn/#next-btn), and both happen before the active class is updated for #supplement-nav li. I will probably have to keep using the MutationObserver.

@WaniKaniJavi Not a bug, just a bit of an unexpected behavior, but I thought I would mention it anyway: I was experimenting a bit with

new MutationObserver(m => console.log(m)).observe(document.getElementById(`lesson`), {childList: true, subtree: true, attributeFilter: [`class`]});

and noticed that when clicking the arrow buttons #prev-btn or #next-btn to go from one item to a different item, the supplement information is first updated, then the tab is switched, and then the supplement information is updated again according to the tab that is now active.

Explanation with screenshots

In this image, I’m on the second item of the lesson batch. I enter

new MutationObserver(m => {debugger}).observe(document.getElementById(`lesson`), {childList: true, subtree: true, attributeFilter: [`class`]});

in the console and then click #next-btn.

After the first DOM modification, the execution pauses and shows that #supplement-nav is not yet updated (still on “Examples”), but the supplement information is already updated (now showing “Vocabulary Examples”, according to the “Examples” tab). Now I press F8 to unpause the execution.

After the second DOM modification, the execution pauses again and shows that #supplement-nav is now up-to-date, and the supplement information was updated again. This is the correct result, but the intermediate step with “Vocabulary Examples” in the second picture seems redundant.

EDIT: Why are two of the three screenshots not showing up correctly? If I click on them, they display correctly, but embedded, they look like this for me:


Is this just on my end?

EDIT 2: Just on my end – nevermind then.

I can see all of your screenshots just fine (on mobile Safari):

1 Like

I can also see all of them just fine (Windows 10, Chrome), so it’s defo just on your end :grinning_face_with_smiling_eyes:


Are you using document.addEventListener('click',...) to add the event listener? I didn’t check how many events are being fired, but at least one event is definitely being fired after the active class is updated, even when I navigate by clicking the next/previous buttons.

You could keep track of the most recent item/tab WKInteraction dispatched a notification about and only notify again if it’s a different item/tab this time. That way, when WKInteraction would still receive two callbacks, but one of them would just return immediately without notifying any subscribing scripts. Then subscribing scripts won’t have to redo work multiple times.

Yes. As an example, I have used the code

let logTab = trigger => console.log(trigger + `: ` + document.querySelector(`#supplement-nav`).textContent);
new MutationObserver(m => logTab(`MutationObserver`)).observe(document.getElementById(`lesson`), {childList: true, subtree: true, attributeFilter: [`class`]});
document.addEventListener(`click`, () => logTab(`Click Event`));
$.jStorage.listenKeyChange(`l/currentLesson`, k => logTab(`jStorage`));

in the console and clicked on next-btn to go to the next lesson. The result was

jStorage: examples
Click Event: examples
MutationObserver: examples
Click Event: examples
MutationObserver: name

So only the second MutationObserver callback happens after the update of the active class, while both click events still see “Examples” as the active tab.


I think in my current version, double notification only happens in the case described earlier:

And the more I think about it, the more I think that this can actually be considered correct behavior, because WaniKani really shows two different tabs in rapid succession.


Yeah, that makes sense.

Another bug/missing feature: I cannot add my own reading/meaning notes (“Click to add note” is missing).

1 Like

Another bug: During kanji lessons, the meaning and the reading mnemonic elements have the IDs supplement-kan-meaning-mmne and supplement-kan-reading-mmne. The mne at the end should only have one m to match the IDs used in the old version.


This should be addressed on preview by a build I pushed out today


Should be fixed in the most recent build I pushed to preview today.


this one should also be fixed


Earlier in this thread, I was talking about attempting to prepare @acm2010’s wk_interaction.js for the upcoming changes, but in the end, I decided to create a new library script instead. “WK Item Info Injector” is intended to simplify the addition/modification of WK item info for any script that wants to use it. For example, to create a script that adds a section below WaniKani’s “Meaning” section showing the item ID during lessons and on the item page, you just need to @require the library script and write

wkItemInfo.on(`lesson,itemPage`).under(`meaning`).append(`Item ID`, o =>;

and it should result in this:



Item Page:

I tried to make the library script useful for as many different use cases as possible, so I tested it with the unmaintained scripts from this list:

For Keisei, Niai, and Rendaku Information, I have the test implementations on my github forks:
Install Keisei Test
Install Niai Test
Install Rendaku Information Test
Unless someone reports any problems with these versions (or my library script), I intend to create pull requests so that the changes are proposed to the original script authors.
Advanced Context Sentence is, as far as I know, not on github, so I have my modifications only locally, but unless there is negative feedback for my library script, I plan to post my code in the respective thread.

My own two scripts affected by the change to React, Mnemonic Artwork and Old Mnemonics, are already using WK Item Info Injector.

I think I should probably create a new thread describing WK Item Info Injector and how to utilize it, but for now I just wanted to see if anyone immediately notices any issues with it. I have only tested it in Edge (Tampermonkey, Violentmonkey) and Firefox (Tampermonkey, Greasemonkey), and I feel very hesitant to suggest to other script authors to rely on such a barely tested library script.


Cool! Sounds like a great resource


I created a new post related to this Script Compatibility Mode coming to WaniKani this week hope this solution works for buying more time while letting us ship this improvement.

Was away for the last two weeks, but will be working on this again starting this week and keeping up with the feedback.

1 Like