# \[For Userscript Authors\] WK Item Info Injector

**URL:** <https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823>\
**Category:** API And Third-Party Apps\
**Created:** [October 3, 2021, 1:00pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823 "2021-10-03T13:00:02Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 3, 2021, 1:00pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/1 "2021-10-03T13:00:02Z")

</div>

> **Table of Contents**
>
> - [What is it?](#heading--what-is-it)
> - [How Can I Use It?](#heading--how-to-use)
> - [Documentation](#heading--documentation)
> - [Selectors](#heading--selectors)
> - [under()](#heading--under)
> - [spoiling()](#heading--spoiling)
> 
> - [Actions](#heading--actions)
> - [Return Value](#heading--return-value)
> 
> - [Usage Examples](#heading--usage-examples)
> - [Security](#heading--security)
> - [Used Version](#heading--used-version)
> - [Features](#heading--features)
> - [Why?](#heading--why)
> - [Feedback and Requests](#heading--feedback-and-requests)

# What Is It?

WK Item Info Injector is a user-created library script that can be used by other userscripts. It simplifies the addition/modification of item info in the user interface of WaniKani.

# How Can I Use It?

WK Item Info Injector can be required in your userscript:

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1951553

```

Alternatively, the end user can be instructed to [install WK Item Info Injector](https://greasyfork.org/scripts/430565-wanikani-item-info-injector) manually – similar to WK Open Framework. The `@require` method is more comfortable for the end user, but comes with a few disadvantages:

- The `@require` meta data has to be kept up-to-date manually (I don’t plan to do many updates for WK Item Info Injector, but they might be necessary every time WK changes its interface, which recently happens pretty often due to the transition to React).
- The entire code of WK Item Info Injector is inserted into every script that `@require`s it, which can increase the parsing time by a few milliseconds.

If WK Item Info Injector is available (by `@require` or by manually installing it), you can use it via the `wkItemInfo` object in the global scope. For example:

```auto
window.wkItemInfo.append("Item ID", o => o.id);

```

inserts a section showing the item ID into the item info in lessons, the lesson quiz, reviews, extra studies, and the item page:

 ![WK_Item_Info_Injector](https://global.discourse-cdn.com/wanikanicommunity/original/4X/7/c/5/7c589f11e21cc0f970dfebaf52d32a143152988f.jpeg)

# Documentation

## Selectors

First, here is an example call that shows (almost) all available options in the _selector chain_:

```auto
wkItemInfo.on("lesson,lessonQuiz,review,extraStudy,itemPage").forType("radical,kanji,vocabulary,kanaVocabulary").under("composition,meaning,reading,examples").spoiling("composition,meaning,reading,examples").append("Info Heading", "Info Body");

```

Everything before `append()` is called the _selector chain_. The selector chain determines when your item info should be inserted. The order of the chain links (selectors) cannot be changed, but selectors that should just use the default arguments can be omitted. The example from above uses the default arguments everywhere, so omitting all selectors has the same result:

```auto
wkItemInfo.append("Info Heading", "Info Body");

```

If you want to inject only during the lesson quiz and the reviews, and only if the current item is a kanji, then you would write

```auto
wkItemInfo.on("lessonQuiz,review").forType("kanji").append("Info Heading", "Info Body");

```

The following table gives an overview of the four available selectors. Except in the case of the `spoiling()` selector, omitting the arguments or omitting the whole selector both default to all accepted values.

| Selector | Accepted Values | Description | Default |
| --- | --- | --- | --- |
| on() | lesson  
lessonQuiz  
review  
extraStudy  
itemPage | Selects the pages on which your info should be injected. | All five page types |
| forType() | radical  
kanji  
vocabulary  
kanaVocabulary | Selects the item types for which your info should be injected. | All four item types |
| under() | composition  
meaning  
reading  
examples | Selects which section your injected info belongs to. By default, this also determines the location where exactly your section will appear. | All four sections |
| spoiling() | composition  
meaning  
reading  
examples  
&nbsp;&nbsp;&nbsp;_or_  
nothing | Defines the sections which your info spoils. `spoiling("nothing")` means that your info spoils no other item info. This selector can usually be omitted. | Omit arguments: same as `spoiling("nothing")`  
Omit selector: same sections as specified in `under()` |

While the information in the table should be enough for basic usage, there are certain intricacies in the behavior of the `under()` selector in combination with the `spoiling()` selector that might be good to know for more control over the injection location and time. Therefore, here is a longer explanation for `under()` and `spoiling()`:

### under()

The behavior in lessons is the most obvious – during lessons, each item’s info is split up into two or four tabs. The keywords map to the tabs of each item type in the following way:

| | composition | meaning | reading | examples |
| --- | --- | --- | --- | --- |
| **Radical** | | Name | | Examples |
| **Kanji** | Radicals | Meaning | Readings | Examples |
| **Vocabulary** | Kanji Composition | Meaning | Reading | Context |
| **KanaVocabulary** | | Meaning | | Context |

For lessons, the `under()` selector specifies the tabs (sections) in which your info should be injected. During the lesson quiz, reviews, extra studies, and on the item page, multiple sections might be displayed at the same time. However, the selector still only matches once, so that your injected info is not duplicated.

> Example: The selector is `under("meaning,reading")` and you visit a kanji item page: The page contains both meaning and reading sections, but the selector still only matches once.

During the lesson quiz, reviews, and extra studies, WK reveals the item info in two steps (for kanji and vocabulary).

> For example, after a vocabulary meaning question, the item info first (step 1) only shows the sections _Related Kanji_ [composition] and _Meaning Explanation/Meaning Note_ [meaning]. Only after expanding (step 2), the sections _Reading Explanation/Reading Notes_ [reading] and _Context Sentence_ [examples] are also shown.

The `under()` selector matches in step 1 if any of the specified sections might appear on screen in step 1 or step 2. However, the `spoiling()` selector can delay the match until step 2.

> With the vocabulary meaning question example from before, `under("meaning,reading")`, `under("meaning")`, and `under("reading")` would all match in step 1, but not in step 2 (no duplication).  
> Important: If the `spoiling()` selector is omitted and defaults to the same keywords as in `under()`, the matches for `under("meaning,reading")` and `under("reading")` will be delayed until step 2. Read about `spoiling()` for more details.

By default, the injected section will be placed in relation to the _last_ applying keyword.

> Example: For a radical’s item page with the selector `under("meaning,reading")`, the last keyword does not apply, but the next-to-last does, so the info will be injected in relation to the meaning section.

### spoiling()

Usually this can just be omitted, but here is a more detailed explanation if you want more control:

The `spoiling()` selector only affects the behavior during reviews, extra studies, and the lesson quiz. In these situations, the default WK interface initially hides some sections of the item info because they would spoil the answer to the opposite question type (at least that’s my interpretation of the behavior – it might also be set up this way just to focus on the more relevant info first).

> Example: After answering a vocabulary meaning question, you open the item info. It only shows the `composition` and the `meaning` sections – `reading` and `examples` are hidden, because they contain spoilers for the reading question.

Only after clicking _Show All Information_, the other sections become visible.

The `spoiling()` selector is able to delay the match until all sections are shown. As long as any of the sections specified in this selector are still hidden, the match will be delayed.

> Continuing the example from before: You use the selector `under("meaning,reading")` and omit the `spoiling()` selector, which defaults to `spoiling("meaning,reading")`. Because `reading` is still hidden, the `spoiling()` selector prevents a match – the injection only happens after clicking _Show All Information_.

## Actions

The selector chain determines _when_ something should happen – the “action” that is appended to the end of the chain determines _what_ should happen. The available actions are:

| Action | Description |
| --- | --- |
| append() | Append the info below the section targeted by `under()`. |
| appendSubsection() | Insert the info as a subsection into the section targeted by `under()`. |
| appendSideInfo() | Only available for meaning or reading. In lessons, reviews, and extra studies, the side info is in a separate column at the left. The info will be appended under the targeted side info. |
| appendAtTop() | Places the info above all the native WK info sections. |
| appendAtBottom() | Places the info below all the native WK info sections. |
| appendSideInfoAtTop() | Only available for meaning or reading. In lessons, reviews, and extra studies, the info is placed at the top of the left column. On the item page, it’s basically the same as `appendAtTop()`. |
| appendSideInfoAtBottom() | Only available for meaning or reading. In lessons, reviews, and extra studies, the info is placed at the bottom of the left column. On the item page, it’s basically the same as `appendAtBottom()`. |
| notify() | No injection, just calls the passed callback function. |
| notifyWhenVisible() | Same as `notify()`, but the callback is only called once the section targeted by `under()` is visible. Use this if you want to modify native WK information. |

All the _append_ actions take two arguments: the info heading and the info body. They can be strings, or DOM elements, or arrays of strings and/or DOM elements. Alternatively, they can also be callback functions returning any of those types, or returning a promise resolving to any of those types.

The callback functions receive as argument the current state, which is an object containing the following properties:

| Name | Description | Example Value |
| --- | --- | --- |
| on | The current page. Corresponds to selector `on()` | `"itemPage"` |
| type | The current item’s type. Corresponds to selector `forType()` | `"kanji"` |
| under | The currently shown sections. Corresponds to selector `under()` | `["meaning"]` |
| hiddenSpoiler | The sections currently hidden but (possibly) appearing later. | `["reading", "examples"]` |
| id | The WaniKani item ID of the current item. | `274` |
| meaning | The primary and alternative meanings of the current item. | `["Narwhal"]` |
| characters | The characters of the current item. N/A for image radicals. | `"金玉"` |
| reading | Accepted readings for the current item. N/A for radicals and kana vocabulary. | `["こう", "く"]` |
| composition | The WK item components of the current item. N/A for radicals and kana vocabulary. | `[{characters: "口",`  
`meaning: ["Mouth"]}]` |
| partOfSpeech | The word types of the current vocabulary item. | `["Noun"]` |
| onyomi | The onyomi readings of the current kanji item. | `["にん", "じん"]` |
| kunyomi | The kunyomi readings of the current kanji item. | `["ひと", "と"]` |
| nanori | The nanori readings of the current kanji item. | `["かず"]` |
| emphasis | The reading type taught in the current item’s kanji lesson. | `"onyomi"` |

The same information can also be obtained with `wkItemInfo.currentState`, but I recommend to use the callback argument whenever possible to decouple the callback function from the actual state (just in case I ever decide to implement preloading, which is however likely not going to happen).

The callback function passed to the `notify()` or the `notifyWhenVisible()` action also gets called with an argument containing these properties. In addition to that, it comes with one more property: `injector`. This object also provides the five _append_ actions that were listed before, but with some differences:

- They don’t accept callback functions as arguments.
- They accept an additional settings object as their third argument.
- They can only be called as long as `injector.active` is `true`. For example, if the user proceeds to the next item, the old injectors will be deactivated.
- They return the created DOM element that will be appended.

The additional settings object currently supports three settings:

| Setting | Description |
| --- | --- |
| injectImmediately | Usually, the section returned by the append action is not yet inserted into the page. If this is set to `true`, the injection is enforced at this point. |
| under | By default, the target section is specified by the `under()` selector. This setting allows to specify any of the available sections (listed in `under` and `hiddenSpoiler`) as the target section. |
| sectionName | At the top of each item page is a list of links pointing to the available info sections. By default, WK Item Info Injector registers appended main sections (not subsections or side info) and uses the heading text content as the link name. This setting allows to choose a different name. |

Aside from the _append_ actions, the `injector` object also offers the function `registerAppendedElement()`. If you manually insert DOM elements (not using the append actions), you can use this function to tell WK Item Info Injector about them. React might not always remove your inserted elements when the page changes, but if you register the element, WK Item Info Injector will make sure that the element gets removed when the user proceeds to the next lesson tab or item.

## Return Value

Registering an action returns an object with the following functions:

| Function | Description |
| --- | --- |
| remove() | Removes the registered action entirely, including its corresponding inserted section. |
| renew() | Removes the corresponding inserted section, generates the section anew and inserts it. Can be called after the user updated some settings to immediately reflect the changes. |

In the case of `notify` actions, elements that were registered with `registerAppendedElement()` are removed as well.

Example for updating the section after a settings change (from the Rendaku Information userscript). When registering the action in `wkItemInfo`, the returned object is stored in `this.itemInfoHandle` for later use:

```auto
this.itemInfoHandle = wkItemInfo.forType("vocabulary").under("reading").appendSubsection("Rendaku Information", o => this.createRendakuSection(o.characters));

```

The settings dialog is created with WK Open Framework and set up to call `this.itemInfoHandle.renew()` when the settings are saved:

```auto
new wkof.Settings({
	script_id: "rendaku_information",
	title: "Rendaku Information Settings",
	on_save: () => this.itemInfoHandle.renew(),
	content: {
		hideTrivial: {type: "checkbox", label: "Hide trivial info"}
	}
});

```

# Usage Examples

WK Item Info Injector is already used in several scripts, some of which are listed here with a short description when they insert their info and example code showing how they use `wkItemInfo`. The last example, “Old Mnemonics”, shows a slightly more complex use case.

> **Mnemonic Artwork userscript**
>
> This script inserts its info for some radicals and kanji. The info consists of a visual mnemonic for the meaning and the reading (both together in one image), so it belongs to both the meaning and the reading section.
> 
> ```auto
> wkItemInfo.forType("radical,kanji").under("meaning,reading").append("Mnemonic Artwork", ({id}) => artworkSection(id));
> 
> ```

> **Keisei userscript**
>
> This script inserts its info for radicals and kanji. During lessons, the info should appear in the “Readings” tab for kanji, and in the “Name” tab for radicals. The info contains the meaning and reading of the current item, so in reviews it should only appear once the item info is fully expanded to avoid spoilers.
> 
> ```auto
> wkItemInfo.forType("kanji").under("reading").spoiling("meaning,reading").notifyWhenVisible(this.injectKeiseiSection.bind(this));
> wkItemInfo.forType("radical").under("meaning").notifyWhenVisible(this.injectKeiseiSection.bind(this));
> 
> ```

> **Niai userscript**
>
> This script inserts its info only for kanji. During lessons, the info should appear in the “Examples” tab, but on all other pages, it should appear below the reading section. The info contains the meaning and reading of the current item, so in reviews it should only appear once the item info is fully expanded to avoid spoilers.
> 
> ```auto
> wkItemInfo.on("itemPage,lessonQuiz,review").forType("kanji").under("reading").spoiling("meaning,reading").notify(this.injectNiaiSection.bind(this));
> wkItemInfo.on("lesson").forType("kanji").under("examples").notify(this.injectNiaiSection.bind(this));
> 
> ```

> **Advanced Context Sentence 2 userscript**
>
> This script does not insert a new section, but modifies the existing context sentence section of vocabulary items. WK Item Info Injector can notify the script whenever a new context sentence section appears on screen, so that the section can immediately be modified:
> 
> ```auto
> wkItemInfo.forType("vocabulary", "kanaVocabulary").under("examples").notifyWhenVisible(() => evolveContextSentence());
> 
> ```

> **KanjiDamage Mnemonics 2 userscript**
>
> This script inserts additional meaning and reading mnemonics for some kanji. They are inserted as subsections for the existing meaning and reading sections.
> 
> ```auto
> wkItemInfo.forType("kanji").under("meaning").appendSubsection(meaningHeading, appendMeaningMnemonic);
> wkItemInfo.forType("kanji").under("reading").appendSubsection(readingHeading, appendReadingMnemonic);
> 
> ```

> **Old Mnemonics userscript**
>
> This script inserts meaning and reading mnemonics for some radicals and kanji. In contrast to the KanjiDamage Mnemonics userscript, for some reason I decided to place the two mnemonics not as subsections, but as two main sections after the reading section (so that the reading mnemonic comes right after the meaning mnemonic). I will show two versions how to achieve this: In the first, both the meaning and the reading mnemonic still belong to “meaning” and “reading”, respectively, but the meaning mnemonic should not use the default location below the meaning section, but should be inserted below the reading section. This requires more control than offered by the _append_ actions: Instead use the `notify()` action and append with the additional settings object `{under: "reading"}` (but only if the reading section exists on the current page – in lessons and/or for radicals, the mnemonic should be inserted below the meaning section).
> 
> ```auto
> wkItemInfo.forType("radical,kanji").under("meaning").notify(o => {
> let heading = o.type === "radical" ? "Old Name Mnemonic" : "Old Meaning Mnemonic";
> let body = oldMnemonicSection(o.id, "meaning");
> o.injector.append(header, body, {under: [...o.under, ...o.hiddenSpoiler].includes("reading") ? "reading" : "meaning"});
> });
> wkItemInfo.forType("kanji").under("reading").append("Old Reading Mnemonic", o => oldMnemonicSection(o.id, "reading"));
> 
> ```
> 
> Alternative version with the “meaning” part split up into more cases to simplify the callback function (with added spaces to make the code easier to read):
> 
> ```auto
> wkItemInfo .forType("radical").under("meaning") .append("Old Name Mnemonic" , o => oldMnemonicSection(o.id, "meaning"));
> wkItemInfo.on("lesson" ).forType("kanji" ).under("meaning") .append("Old Meaning Mnemonic", o => oldMnemonicSection(o.id, "meaning"));
> wkItemInfo.on("itemPage,lessonQuiz,review,extraStudy").forType("kanji" ).under("reading").spoiling("meaning").append("Old Meaning Mnemonic", o => oldMnemonicSection(o.id, "meaning"));
> wkItemInfo .forType("kanji" ).under("reading") .append("Old Reading Mnemonic", o => oldMnemonicSection(o.id, "reading"));
> 
> ```

# Security

WK Item Info Injector assumes that everything on the WaniKani page is trustworthy. This might not be the case if the user has installed a malicious userscript, a malicious browser extension, or a userscript with an XSS vulnerability. The script does not sanitize data read from the DOM tree or from jStorage and just passes it on to the callback functions. Furthermore, if a sandboxed script (anything with `@grant` other than `none`) `@require`s WK Item Info Injector, it might use `unsafeWindow` to install `wkItemInfo` into the global page context. For more details about this security concern, you can read [my discussion with](https://community.wanikani.com/t/52617/43) @est_fills_cando about this topic.

I am of the opinion that the risk of malicious code being executed on the WaniKani site is low enough to ignore this possibility, but if you have a different opinion let me know.

# Used Version

WK Item Info Injector is placed in the global scope of the webpage, so usually all scripts use the same instance. This makes the order in which the additional sections are appended deterministic. An outdated instance in the global scope is however replaced if a newer version of WK Item Info Injector is executed.

> Example: Script A `@require`s version 1.0 of WK Item Info Injector, and Script B `@require`s version 1.1. If Script A executes before Script B, then Script A will install version 1.0 into the global scope, but Script B will later replace it with version 1.1. If at that point, Script A has already registered a selector in version 1.0, then this selector will continue to be handled by version 1.0. Both version 1.0 and 1.1 will continue to run in any case.  
> If Script B executes before Script A, then Script B will install version 1.1, and Script A will also use the existing version 1.1, which should not cause a problem because all versions should be backwards compatible.

Consequentially, if the user has installed WK Item Info Injector directly in their script manager, it should automatically be kept up-to-date, and as long as it runs before all `@require`-ing scripts, all of them will use the same (the newest) version. WK Item Info Injector will by default run before most other scripts because it uses `@run-at document-start`, but if there are `@require`-ing scripts that also use `@run-at document-start`, WK Item Info Injector has to be placed above those other scripts in the script manager.

# Features

- Simplified injection of item info
- Usually deterministic order of injected sections (depending on the order in which the actions were registered)
- Adds links at the top of item pages that point to injected main sections
- Still works on item pages if not logged into WaniKani
- Still works even if the user has Tampermonkey disabled on page load and enables it later
- Tries to minimize browser reflow by inserting sections in batches
- Adds a side info bar to the meaning tab of radical lessons if required
- Easily remove or recompute your inserted section, e.g. after the user changed a setting

# Why?

Aside from hopefully being useful for other script authors, the main reason for the creation of this library script are the extensive changes of the WaniKani page structure caused by the ongoing transition to React. Two months ago, [a change to the lesson page](https://community.wanikani.com/t/52617) caused several scripts to break: Two of them were my own, but also popular scripts like Keisei and Niai were affected. Most of the broken scripts were unmaintained, so while I was updating my two scripts, I got the idea that I could instead move the injection functionality into a separate library script, which I could then use to quickly fix all the unmaintained scripts.

# Feedback and Requests

If you need additional features, ask here in this thread and I will think about adding them. I’m also interested to hear what I could have done better in the design of the interface (selector chain + action), but I probably won’t change it because everything should stay backwards compatible. Also let me know if any part of the documentation is unclear, or if the script shows some unexpected behavior. Lastly, I’m not a native English speaker, so my wording might be awkward or grammatically incorrect – feel free to also correct me on that.

---

<div class="post-metadata">

**Author:** ![psdcon](https://cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/wanikanicommunity/original/3X/f/d/fd4c154120954695f788402f3bcf4e616499bc2d.png) [@psdcon](https://community.wanikani.com/u/psdcon)\
**Post date:** [October 16, 2021, 5:13pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/2 "2021-10-16T17:13:57Z")

</div>

Developing [Anime Sentences](https://community.wanikani.com/t/userscript-anime-context-sentences/54003) was 1000 times easier thanks to this library. Thank you so much @Sinyaven!

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [November 6, 2021, 2:33pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/3 "2021-11-06T14:33:16Z")

</div>

Version 1.2:

> **Now works with the recent update to the lesson quiz**
>
> Recently, [the additional information panel in the lesson quiz was converted to React](https://community.wanikani.com/t/54242). WK Item Info Injector should now work again during the lesson quiz (both with and without script compatibility mode).
> 
> In the React version, the item info is created when the user proceeds to a new item (in the old version, it was only created once the user opened the item info). I decided to match the new WK behavior and also inject the info at that point instead of waiting for the user to open the item info. This speeds up opening the (unexpanded) item info, but causes a lot of unnecessary injections (for _every_ review instead of just on demand). I’m not sure if this was the best solution or if I should change it in the future – especially once the review page is also converted to React.

> **Kanji lesson meaning side info column added on demand**
>
> During kanji lessons, [the side info column is not rendered anymore if there are no meaning synonyms](https://community.wanikani.com/t/54242/9). WK Item Info Injector now adds that column on demand (as was already done in the case of radical lessons) so that it is still possible to inject item info in this location.

> **Minor bugfixes/improvements**
>
> - In some cases, side info was injected at the wrong place on item pages.
> - WaniKani now displays side info in a darker text color (`class="text-gray-700"`). WK Item Info Injector now automatically adds this class to injected elements.

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=985948

```

* * *

Can someone explain to me how userscript authors are supposed to make use of `WaniKani.version`? I was expecting the compatibility mode to be on an older version number, but both modes are on the same version. `WaniKani.wanikani_compatibility_mode` can be used to differentiate between compatibility mode on/off. But if the version number is always the latest, is the version number even useful in any way?

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [January 30, 2022, 1:43pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/4 "2022-01-30T13:43:00Z")

</div>

Version 1.3:

> **Added two new actions**
>
> The new actions `appendAtBottom()` and `appendSideInfoAtBottom()` are the counterparts to the established actions `appendAtTop()` and `appendSideInfoAtTop()` and can be used to place your injected section below all native WK sections.

> **Bugfix related to the relatively new lesson tab "Word Use"**
>
> Some vocabulary items have a fifth lesson tab “Word Use”. I have not added support for this tab yet – you cannot inject sections into this tab using WK Item Info Injector, and while the user is on this tab, `wkItemInfo.currentState.under` is `[undefined]`. But at least the fifth tab should not affect the established injection functionality anymore.

> **Bugfix for endless loop**
>
> During lessons, WaniKani does not remove the lesson quiz elements. If these elements are changed while the user is actually doing lessons (and not the lesson quiz), the MutationObserver in WK Item Info Injector would trigger while in an unexpected state and possibly execute registered callback functions (actions).
> 
> The registered action of the WaniKani Pitch Info script changes every reading element on the page – also the reading elements in the hidden lesson quiz. This change triggered the MutationObserver, which in turn called the WaniKani Pitch Info action again, creating an endless loop.

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1013941

```

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [March 3, 2022, 9:16pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/5 "2022-03-03T21:16:24Z")

</div>

Version 1.4:

> **Now supporting extra study page**
>
> [Today WaniKani has added a new functionality: extra study.](https://community.wanikani.com/t/56078) This update adds support for this new page. The `.on()` selector now also accepts the keyword `extraStudy`.

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1024045

```

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [June 5, 2022, 2:12pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/6 "2022-06-05T14:12:28Z")

</div>

Version 1.5:

> **Now supporting the accordion UI in the lesson quiz**
>
> A while ago, WK updated the lesson quiz again – this time they changed the item info display into an accordion. It took me a while to decide where the sections should be injected: should `append()` insert the section into an existing collapsible section, or should it create a new collapsible section? In the end, I decided to follow the initially intended semantics of my interface so that `append()` creates a new, more or less independent section (therefore a new collapsible section), while `appendSubsection()` or `appendSideInfo()` extends one of the existing sections (therefore injected into an existing collapsible section).
> 
> In case the number of collapsed sections gets too high and it becomes annoying clicking each one to open them, it is possible to use my [Spacebar Expand All](https://greasyfork.org/scripts/446033-wanikani-spacebar-expand-all) script to “restore” the old spacebar functionality of showing the entire info section.
> 
> The main sections are injected once the user opens the item info. The subsections and side info within vanilla WK sections are injected once the user opens the corresponding WK section.
> 
> An interesting detail I noticed while working on this: Until now, WK has put the side info _Word Type/Parts of Speech_ into the Meaning section, but in the new lesson quiz layout, this side info is in the Context section.

> **IMPORTANT: change of behavior**
>
> I had to slightly change the interface provided by WK Item Info Injector to make it work with the accordion UI: In addition to the `notify()` action, there is now a `notifyWhenVisible()` action. While `notify()` calls the callback function as soon as the user shows the item info (so that the callback function can add more collapsible sections if needed), `notifyWhenVisible()` calls the callback function when the targeted section is expanded (so that the callback function can modify native WK information; e.g. _Advanced Context Sentence 2_ that modifies the WK context sentences).
> 
> Also, calling `injector.appendSubsection()` within the callback when the targeted section is collapsed warns in the console that the subsection could not be injected. But the subsection will be cached and injected once the targeted section gets expanded, so it should still work despite the warning. Maybe I will remove the warning in a future version. However, `injector.appendSubsection(h, b, {injectImmediately: true})` **will also _not_ inject immediately** , so use `notifyWhenVisible()` if you need `injectImmediately`.

> **Greasemonkey compatibility**
>
> Greasemonkey does not automatically provide access to variables stored in the page’s `window` object – you would have to explicitly use `unsafeWindow.WaniKani.wanikani_compatibility_mode` to check if the page is in script compatibility mode, or `unsafeWindow.$.jStorage` to access the jStorage loaded by WaniKani. Instead of using `unsafeWindow` or loading a new instance of jStorage into the script context, I decided to instead try out the `window.eval()` method [described in the Greasemonkey wiki](https://wiki.greasespot.net/Content_Script_Injection).

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1057854

```

---

<div class="post-metadata">

**Author:** ![Gorbit99](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/gorbit99/32/7307_2.png) [@Gorbit99](https://community.wanikani.com/u/Gorbit99)\
**Post date:** [June 16, 2022, 12:28am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/7 "2022-06-16T00:28:57Z")

</div>

Nevermind, just didn’t understand it fully I don’t think

Is it possible to get a callback call for every section besides defining it for each section separately? So let’s say I want to get both reading and meaning, and instead of appending some section, I want to modify the contents of these, so I put in “meaning,reading” for “under”, and then currently in reviews I either get a callback for “reading” or “meaning”, whichever comes first, but is it possible to get first a callback for meaning, and then reading, in case first only reading was visible?  
~~Currently this could be done with:  
wkItemInfo.under(“reading”).spoiling().notify(…);  
wkItemInfo.under(“meaning”).spoiling().notify(…);~~  
I only assumed this would work, but it actually doesn’t, just calls the callback twice for one section then stops.

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [June 16, 2022, 11:31am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/8 "2022-06-16T11:31:51Z")

</div>

> [@Gorbit99](#):
>
> Is it possible to get a callback call for every section besides defining it for each section separately?

No, for this you have to register it separately per section. Do you have a use case where this poses a problem?

> [@Gorbit99](#):
>
> so I put in “meaning,reading” for “under”

If you use `wkItemInfo.under("meaning,reading")` in reviews, this means that it will target the reading section (because it is the last in the list), except if it’s a radical that doesn’t have a reading section, then it targets the meaning section. Such a selector is useful if you want to append your section below all vanilla WK mnemonics, but can not be used for your use case.

> [@Gorbit99](#):
>
> Currently this could be done with:  
> wkItemInfo.under(“reading”).spoiling().notify(…);  
> wkItemInfo.under(“meaning”).spoiling().notify(…);

If you tell Item Info Injector that your section does not spoil anything then it won’t delay the action until the targeted section is visible. So I think you should remove the `spoiling()` selector.

* * *

So if you want to modify the content of the vanilla WK meaning section in your callback you would use:

```auto
wkItemInfo.under("meaning").notifyWhenVisible(yourCallback);

```

`notifyWhenVisible()` and `notify()` are almost the same – they only differ during the new lesson quiz where the section headings are always immediately visible while the content might still be collapsed. Since you want to modify the content, you have to use `notifyWhenVisible()` for it to work during the lesson quiz.

---

<div class="post-metadata">

**Author:** ![Gorbit99](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/gorbit99/32/7307_2.png) [@Gorbit99](https://community.wanikani.com/u/Gorbit99)\
**Post date:** [June 16, 2022, 12:34pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/9 "2022-06-16T12:34:02Z")

</div>

When I tried to define it per section, for some reason it was like the first that gets called also called the other callback, if that makes sense? So if I had

```auto
wkItemInfo.under("reading").notify(...)
wkItemInfo.under("meanign").notify(...)

```

Both of them got called with state.under being [“meaning”, “composition”] or [“reading”, “composition”], whichever came first.  
Also, not having spoiling made it so I needed to open all of the information to work I think

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [June 16, 2022, 12:49pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/10 "2022-06-16T12:49:43Z")

</div>

> [@Gorbit99](#):
>
> When I tried to define it per section, for some reason it was like the first that gets called also called the other callback, if that makes sense?

Are you sure that this also happened when you did _not_ include the `spoiling()` selector?

> [@Gorbit99](#):
>
> Also, not having spoiling made it so I needed to open all of the information to work I think

I assume you tried

```auto
wkItemInfo.under("meaning,reading").notify(yourCallback)

```

and `yourCallback` was only called once the user showed all information. The reason this happens is because if you “hook” your action to meaning _and_ reading, then Item Info Injector by default assumes your info spoils meaning and reading, so it delays the action until all info is viewed. I don’t know what exactly you want to achieve, but I think everything becomes less confusing if you do not use multiple values in `under()` (and don’t use `spoiling()` at all because you probably don’t need it). If you want, you can describe your use case and I can tell you how/if it can be done with Item Info Injector.

---

<div class="post-metadata">

**Author:** ![Gorbit99](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/gorbit99/32/7307_2.png) [@Gorbit99](https://community.wanikani.com/u/Gorbit99)\
**Post date:** [June 17, 2022, 2:57am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/11 "2022-06-17T02:57:00Z")

</div>

This one works, it seems the spoiling() was my issue, thanks a lot!

---

<div class="post-metadata">

**Author:** ![polv](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/polv/32/572691_2.png) [@polv](https://community.wanikani.com/u/polv)\
**Post date:** [October 5, 2022, 8:31am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/12 "2022-10-05T08:31:50Z")

</div>

Crashes for radical itemInfo page, e.g.

```auto
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.wanikani.com/radicals/poop
// @icon https://www.google.com/s2/favicons?sz=64&domain=wanikani.com
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1057854
// @grant none
// ==/UserScript==

(function() {
    'use strict';

wkItemInfo
    .on('itemPage')
    .append('blahblaj', (state) => {
      return document.createElement('hr');
    });
})();

```

Brave Browser, Windows 11, Tampermonkey

![image](https://global.discourse-cdn.com/wanikanicommunity/original/4X/e/5/2/e520690079e601fd0efd52006d95669d7b8b1225.png)

 ![image](https://global.discourse-cdn.com/wanikanicommunity/original/4X/b/5/b/b5b8ca223041e5a39ea928d0b991e323b53dfac9.png)

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 5, 2022, 6:42pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/13 "2022-10-05T18:42:08Z")

</div>

Thanks for letting me know that they have changed the Radical item pages. I will try to update my script as soon as possible.

I was hoping it would still work with compatibility mode enabled, but the change seems to be in both versions. I’m wondering if this is a mistake or if they have abandoned this approach. Their [webpage about the compatibility mode](https://knowledge.wanikani.com/wanikani/script-compatibility-mode/) is also pretty outdated.

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 5, 2022, 10:45pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/14 "2022-10-05T22:45:40Z")

</div>

Version 1.6:

- Updated to support new React-based radical item info page

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1101319

```

---

<div class="post-metadata">

**Author:** ![polv](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/polv/32/572691_2.png) [@polv](https://community.wanikani.com/u/polv)\
**Post date:** [October 5, 2022, 11:51pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/15 "2022-10-05T23:51:02Z")

</div>

`class="subject-section __text"` is missing, so styling remains broken. I am not sure if `class="subject-section__ subsection"` is also needed.

 ![image](https://global.discourse-cdn.com/wanikanicommunity/original/4X/9/4/a/94a7da3015c7198aeca900ec4151cac14966c538.png)

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 6, 2022, 5:21am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/16 "2022-10-06T05:21:45Z")

</div>

Version 1.6 only added this class if the user only provided a string and not their own elements for the info body. I was not sure if I should also modify the elements provided by the user by automatically adding the class `subject-section__text` to them, but it’s probably useful to do this.

* * *

Version 1.7:

- Automatically add class `subject-section__text` to info body elements on item pages

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1101385

```

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 9, 2022, 9:44am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/17 "2022-10-09T09:44:25Z")

</div>

Version 1.8:

> **Reverted the change in v1.7 and instead copied CSS from WK to apply it to the parent element**
>
> I want to avoid tampering with the user-provided elements as much as possible to avoid unexpected behavior. With version 1.7, a user that wants to create a new section which also has its own subsections might write something like this:
> 
> ```auto
> let heading = document.createElement("h3");
> heading.textContent = "Subsection Heading";
> heading.classList.add("subject-section__subtitle");
> let p = document.createElement("p");
> p.textContent = "Subsection text";
> wkItemInfo.appendAtTop("Test", [heading, p]);
> 
> ```
> 
> but because version 1.7 automatically adds the class `subject-section__text` to every provided element – including the `h3` element – the heading is not styled as a subsection heading but as regular text. Therefore, I reverted this behavior and instead added WaniKani’s regular text styling to the CSS for the parent `section.item-info-injector` element which can easily be overridden by the user.

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1102710

```

---

<div class="post-metadata">

**Author:** ![polv](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/polv/32/572691_2.png) [@polv](https://community.wanikani.com/u/polv)\
**Post date:** [October 9, 2022, 9:49am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/18 "2022-10-09T09:49:49Z")

</div>

Is it possible to either append below or append at top, depending on callback logic?

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 9, 2022, 10:08am UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/19 "2022-10-09T10:08:16Z")

</div>

For this, you have to use the `notify()` action. For example:

```auto
wkItemInfo.forType("radical").spoiling("nothing").notify(state => {
	if (state.meaning[0].startsWith("B")) {
		state.injector.appendAtTop("Heading", "Body");
	} else {
		state.injector.appendAtBottom("Heading", "Body");
	}
});

```

For radicals with a name starting with the letter B, the section is inserted at the top, and for other radicals it is inserted at the bottom.

---

<div class="post-metadata">

**Author:** ![Sinyaven](https://sea1.discourse-cdn.com/wanikanicommunity/user_avatar/community.wanikani.com/sinyaven/32/190443_2.png) [@Sinyaven](https://community.wanikani.com/u/Sinyaven)\
**Post date:** [October 11, 2022, 5:57pm UTC](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823/20 "2022-10-11T17:57:37Z")

</div>

Version 1.9:

- Updated to support new React-based Kanji item info page

```auto
// @require https://greasyfork.org/scripts/430565-wanikani-item-info-injector/code/WaniKani%20Item%20Info%20Injector.user.js?version=1103761

```

[Next page](https://community.wanikani.com/t/for-userscript-authors-wk-item-info-injector/53823.md?page=2)
