Parts of Lessons converted to React now in Preview

Good catch, fixed that earlier today.


Oh no, I love all of the scripts you mentioned here
(and now the two of them that were bugged for me are working perfectly because of your fixes, thank you so much for that btw!)

Quite a lot of the scripts on WK are made by people who aren’t around anymore to update them, right? Doesn’t sound good if this update will break many of them :slightly_frowning_face:



On the preview site, audio on the reading lesson card now plays twice sometimes, but not all the time. It either plays once, followed by a short delay, then again, or it plays overlapped with the second play started before the first one ends. It might be that in the instances where it doesn’t seem to play twice, it is actually playing twice, but starts at the same time so you don’t notice it. (Not sure, just a guess.) I have confirmed this happens with all scripts disabled.

Also, sometimes it is more than 2 times. Like as I’m writing this, it’s playing doubled up audio for 掲載 once every 20 seconds or so even though I’m not interacting with the page at all. (8 total playbacks, 2 overlapped after clicking the reading tab, then 2 overlapped once every 20 seconds or so three times)


Can someone confirm that the Lesson Filter script works? I don’t have any lessons available to test it myself.

I have had this issue on the current site as well. Not every time I play the audio though, it’s pretty rare.

Two more bugs I found during lessons on the preview server (with userscripts disabled):

  • When I’m on a vocab item “Reading” tab and switch to a different vocab item by clicking on it in the list at the bottom, it autoplays this item’s audio.
  • When I’m on a kanji item’s “Examples” tab and press Enter, instead of going to the next item, it switches back to the current item’s “Readings” tab. This seems to only happen in a specific circumstance: The first completed lesson in this batch has to be a radical. I have added a screenshot below showing a situation (RKKRR) where this problem occurs. If I get KKRRR and continuously hit enter, it works correctly. But if I jump to a radical before completing the first K, and later come back to the K, the bug occurs. Similarly, if I get RRKRR and immediately jump to the K lesson, it works, but if I first complete an R lesson, it breaks. (sorry for the wordy explanation; I hope it was still understandable)

The double autoplay audio bug also happens to me. Sometimes they play delayed, making it more noticeable, and sometimes they play at the same time, making it just louder.

On another note: Since @acm2010 (the original script author) is not around anymore, I have looked into fixing wk_interaction.js that was created for the Keisei and Niai scripts, and is also used by the Rendaku Information script. However, React makes it really hard to reliably tell when a lesson tab switch happens. Until now, wk_interaction.js used a MutationObserver to detect when the supplement div changes, but React sometimes just re-uses existing elements, so this approach does not seem sufficiently reliable. It also seems like on a tab switch, React does not always remove elements that were added to the old tab by a script, so scripts now have to do that themselves.

My current solution for wk_interaction.js contains

$.jStorage.listenKeyChange("l/currentLesson", () => this.lessonInfoCallback());
this.lessonInfoObserver.observe(document.getElementById(`lesson`), {childList: true, subtree: true});

The MutationObserver is for detecting tab changes, but it misses changes where the tab stays the same, and only the current lesson changes – this can happen for example if you are at the “Name” tab (the first tab) of a radical and then jump to a different radical by clicking on it at the bottom. For the new radical, the tab is still the “Name” tab, and the MutationObserver only reports small content changes that I think can’t be discerned from changes made by other userscripts, which I think should not trigger anything. To catch these cases, I added a jStorage change listener that triggers when the lesson changes, but this means that if the tab and the current lesson change at the same time, the script triggers twice. So in my solution, Keisei generates and adds its information section, then immediately deletes it and generates and adds it again.

It works, but it’s not ideal. If anyone has better suggestions/solutions for detecting tab changes, let me know – otherwise I will leave it at that and create a pull request with this mediocre fix.

1 Like

How about creating a test account? You would start at level one on such an account and plenty of lessons would be available.

Would a test account be against terms of service? :thinking:
If yes, then this is definitely not a test account. Not that anyone could guess what my main account is, anyway. There is definitely nothing that would give it away.


Hey aren’t you that person that made Sergues Sionfucon


Which portion of the terms of service do you refer to?

I didn’t any such restriction in the terms of service. Tofugu wants the scripts from the API to work. I don’t see why they would object to a test account.

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.