Wikipedia:User scripts/Guide#Publishing a CSS file

{{Wikipedia programs}}

{{Shortcut|WP:US/G}}

{{About|writing user scripts for use on Wikipedia|text=For instructions on how to install user scripts, see How do you install user scripts?}}

Prerequisites

{{Wikibooks|JavaScript}}

To write user scripts, you will have to learn at least some of the programming language that they are written in: JavaScript.

Try these links:

  • [https://developer.mozilla.org/docs/Web/JavaScript Mozilla Developer Network's JavaScript site]
  • [http://www.w3schools.com/js/ W3schools Javascript tutorial]
  • JavaScript syntax
  • jQuery

Also, it would definitely help if you tried using one of our scripts and got it working. The rest of this tutorial assumes you know where the various things are (all explained at {{Section link|Wikipedia:User scripts#How do you install user scripts?}}).

Forking an existing script

Starting out, it may be easier to modify an existing script to do what you want, rather than create a new script from scratch. This is called "forking". To do this, copy the script to a subpage, ending in ".js",The actual requirement is that the page have contentmodel "javascript". Making a page whose title ends in ".js" will automatically give it that content model and indicates to readers that the page contains JavaScript. of your user page. Then, install the new page like a normal user script.

Writing a script from scratch

Although you can write a script directly in your common.js page or skin.js (such as vector.js) page, it is usually better to create a new subpage for it in the form YourUserName/title.js, where title is the name of your script. That keeps your main js pages from getting cluttered and is helpful when you have multiple scripts installed. You will also want to install the new user script.

= Hello world =

To make a Hello world program, insert the following code into your User:YourUserName/common.js file:

importScript('User:YourUserName/hello-world.js');

Next, create the page User:YourUserName/hello-world.js, and insert this code:

$('#bodyContent').prepend('

Hello world!

');

This will write "Hello world!" on every page, below the title, until you remove the code. User scripts are written in JavaScript, and both of the above code snippets are in JavaScript. The second snippet uses JQuery, a JavaScript library that specializes in manipulating HTML. $ is a JQuery function that lets us target the HTML element we want. #bodyContent is a string in CSS selector syntax, and means target the HTML element with property id="bodyContent" . prepend is a JQuery function that inserts HTML code as a child of the #bodyContent element.

Hello world!

is the HTML code to be inserted.

= Your first script =

We will be writing a user script by modifying your common.js. For the purpose of this tutorial, we will write a simple version of the Quick wikify module, which adds the {{Wikify}} maintenance template to the top of an article when you click a link called "Wikify" in the "More" menu. To begin, change MODULE_NAME in the module template to "Qwikify". Your template should look like this:

// Qwikify

$( document ).ready( function () {

MODULE_CODE;

} );

In MODULE_CODE, we want to add the "Wikify" tab, so we will use the addPortletLink() function (requiring the mediawiki.util module). Replace MODULE_CODE with a call to this function. Then we will bind an event handler so that when this link is clicked, we will call another function named doQwikify() that will actually execute the code. The name is what is shown on the tab, so set that to 'Wikify'. Most tabs have an ID of ca-name, so set the ID to 'ca-wikify'. The title (also known as mouseover or rollover text) should be something like 'Mark for wikification'.

Lastly, we use jQuery's [//api.jquery.com/click/ .click()] to listen for clicks on this link, and when that happens, execute a function. After we call doQwikify(), it says event.preventDefault(). Since we clicked on a link, we need to tell the browser to prevent its default behavior (going to the URL, '#'). We want the page to stay right where it is at, so to prevent the browser from following the link, we prevent that and do our own custom action.

Altogether, your new function should look like this:

// Make sure the utilities module is loaded (will only load if not already)

mw.loader.using( 'mediawiki.util', function () {

// Wait for the page to be parsed

$( document ).ready( function () {

// See the "Portlets (menus and tabs)" subsection below

var link = mw.util.addPortletLink( 'p-cactions', '#', 'Wikify', 'ca-wikify', 'Mark for wikification');

$( link ).click( function ( event ) {

event.preventDefault();

doQwikify();

} );

} );

} );

Now, we must write our actual doQwikify() function. It will edit the edit box, so we need to get the name of that and its form. Viewing the source of the page shows that the form is named editform and the textbox is named wpTextbox1, meaning that the actual text is document.editform.wpTextbox1.value. To add {{tl|wikify}} (and two new lines), we simply do:

document.editform.wpTextbox1.value = "{" + "{wikify}}\n\n" + document.editform.wpTextbox1.value;

(We separate the two "{" brackets in the front of the wikify template so it doesn't get expanded when we write this code on the wiki.)

Finally, we want to submit the form for the user. Luckily, JavaScript has a built-in function just for this named submit(). To submit our editing form, use document.editform.submit(). Your code should now look something like this:

function doQwikify() {

document.editform.wpTextbox1.value = "{" + "{wikify}}\n\n" + document.editform.wpTextbox1.value;

document.editform.submit();

}

And that's it! Combine it all together and it should look like this:

// Make sure the utilities module is loaded (will only load if not already)

mw.loader.using( 'mediawiki.util', function () {

// Wait for the page to be parsed

$( document ).ready( function () {

// See the "Portlets (menus and tabs)" subsection below

var link = mw.util.addPortletLink( 'p-cactions', '#', 'Wikify', 'ca-wikify', 'Mark for wikification');

$( link ).click( function ( event ) {

event.preventDefault();

doQwikify();

} );

} );

} );

function doQwikify() {

document.editform.wpTextbox1.value = "{" + "{wikify}}\n\n" + document.editform.wpTextbox1.value;

document.editform.submit();

}

Save this to your User:YourUserName/common.js page. Then go visit a page such as the Sandbox, go into the "More" menu, click "Wikify", and watch the user script add the maintenance tag for you.

Built-in scripts

All Wikipedia pages include some built-in MediaWiki JavaScript code, with variables and functions that can be used in user scripts. Some of them were already mentioned ($(), importScript(), mw.util). This code is generally loaded as ResourceLoader modules (some of it preloaded, some loaded on demand) and ends up in properties of these globally available objects:

  • mw (mediaWiki) for MediaWiki core,
  • $ (jQuery) for jQuery,
  • OO for OOjs.

Some commonly accessed properties of mw include mw.config, mw.user.options, mw.util, mw.Title, mw.loader, and mw.hook. OO.ui is the namespace of OOUI. See mw:ResourceLoader/Core modules for more details.

Development and testing

The following development environments can be used to develop and test your script.

= Basic =

  • Using the preview button: You can edit your script directly on your /common.js page, then click [Show preview] and the new code is executed right away on the preview page.
  • Saving it: If required elements are missing on the preview page (for example, your script does something on history pages), you will have to save the script in order to test it. However, it is not convenient and creates unnecessary entries in the page history.
  • Execute it in your browser's JavaScript console: All modern browsers come with a JavaScript console and other development tools. You can type or paste and execute your code there; script errors and warnings will also be shown there. How to open the console depends on your browser:
  • In Google Chrome and Edge – press {{press key|Ctrl|Shift|J}}
  • In Firefox – press {{press key|F12}}
  • In Safari – press {{press key|Ctrl|Alt|C}}

:You may need to click the Console tab if a different pane is currently open.

= Loading it from a localhost web server =

The best and most recommended way to load a JavaScript file during development is from your local web server (see below for an easy way to install a web server). Put this string in your /common.js:

mw.loader.load( 'https://localhost/wikipediatest.js' );

In some environments, you need to write this as:https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content

mw.loader.load( 'http://127.0.0.1/wikipediatest.js' );

Then run any web server on your computer and create the wikipediatest.js file in the appropriate folder. The code inside this file will be executed as if it was inside your personal script.

You can edit your wikipediatest.js file with any text editor, perhaps with syntax highlighting and other convenient features, save the file and simply reload any Wikipedia page to see the results. (You do not need to wait, and if your web server is nice or you set it right, you do not even need to bypass your browser cache.)

Most modern code editors and IDEs allow you to set up a localhost server – eg. use [https://atom.io/packages/atom-live-server atom-live-server] in Atom, and [https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer Live Server] in VS Code. WebStorm and PhpStorm have the feature built in, without requiring an extension. You can also use a third party program such as Node.js's npx http-server command ([https://www.youtube.com/watch?v=hnTiNvv5Eec video tutorial]), or XAMPP.

If you have Python installed, you can run python -m http.server from command-line from the folder your script is in.

On Windows, you could also use for example [https://www.ritlabs.com/en/products/tinyweb/download.php TinyWeb], less than 100 kbyte on disk and not requiring installation. Save and unzip tinyweb.zip for example into c:\Program Files\Tinyweb, create a shortcut to tiny.exe, and add an argument in shortcut properties — path to your folder with wikipediatest.js and any file index.html (required). Start TinyWeb with this shortcut; unload it with Task Manager.

Note that this method doesn't work in Opera 9.50 (and later) due to added security restrictions, see [http://www.opera.com/docs/changelogs/windows/950/ Opera 9.50 for Windows changelog]: "Local servers can use remote resources, but not vice versa". In Chrome, it may be necessary to [http://stackoverflow.com/a/25075349/6357045 enable SSL], otherwise the script will refuse to load.

= Browser-specific =

Some browsers allow you to automatically execute your JavaScript code on specific web pages. This way you do not have to be logged in to Wikipedia. One example is Tampermonkey. However, making user scripts work with one of these extensions might require some modifications to the script code.

= Running pieces of code =

You can run pieces of code on already loaded pages via the JavaScript console. See the [https://developer.chrome.com/docs/devtools/console/javascript/ guide for doing this in Chrome]. It works similarly in most other browsers. In addition, Chromium-based browsers have a [https://developer.chrome.com/docs/devtools/javascript/snippets snippets] feature where short pieces of JavaScript code can be saved and debugged.

Publishing

Once you have finished the user script code, you can save it as a page so that others can import it. By convention, scripts are in your userspace and have titles ending in ".js", for example "User:YourUsernameHere/MyCoolScript.js". Others can then install the new script.

Text editors and debugging

= Text editors =

You can use anything from a simple text editor, to a more feature-packed code editor or IDE. Here are some recommended editors, by operating system.

= JavaScript Debuggers =

These are typically built into browsers, in their DevTools window. Debuggers allow you to step debug (go through your JavaScript code line-by-line, hover over variables to see their values, etc.)

  • Firefox - use Tools → JavaScript Console showing all JavaScript and CSS errors.
  • Chrome and Edge - use Tools → Developer Tools.
  • Safari - Safari → Preferences → Advanced and enable the "Show Develop menu in menu bar" option. Then use Develop → Show Web Inspector to open up the development tools.
  • Opera - use Tools → Advanced → Error Console showing all JavaScript and CSS errors.

Basic techniques

= Running code on page load =

The personal user module (built from /common.js, /common.css and optionally the skin-specific files for the current skin; see above) and gadgets are loaded on all pages. Most scripts will want to manipulate elements on the page; to do so the page needs to be ready (it may not be the case at the time the modules are loaded). We can defer execution of code by using a special function.

== <code>$(document).ready(...)</code> ==

One option is [//api.jquery.com/ready/ .ready()] from jQuery.

// Define our main function

function myScript() {

// ... code ...

};

// Schedule it to run after the HTML page is parsed

$( document ).ready( myScript );

// This shorthand is also valid

jQuery( myScript );

Since the function is called only once, many users prefer to shorten this code with an anonymous function:

$( document ).ready( function () {

// ... code ...

} );

// Or

jQuery( function () {

// ... code ...

} );

Note: $ and jQuery are the same object; choosing between them is purely a matter of opinion.

Many scripts use this function simply to add some script interface, such as a link in a portlet. Then the main part of the code is executed after the user clicks on that link.

== <code>mw.hook('wikipage.content').add(...)</code> ==

However, if your code works with the content part of the page (the #mw-content-text element), you should use the [https://doc.wikimedia.org/mediawiki-core/master/js/Hooks.html#~event:'wikipage.content' 'wikipage.content'] hook instead. This way your code will successfully reprocess the page when it is updated asynchronously and the hook is fired again. There are plenty of tools that do so, ranging from edit preview to watchlist autoupdate.

Be sure to only work with the descendants of the $content element that your handler function takes and not the whole page. Otherwise, you may end up running the same code for the same elements many times. Note that the 'wikipage.content' hook may be fired really many times.

Be cautious about what comes in the $content argument of the handler function. You should not assume it is the #mw-content-text element. It can be a small portion of the page, e.g. when it is previewed.

Code that works with page content and avoids the aforementioned pitfalls may look like this:

mw.hook( 'wikipage.content' ).add( function ( $content ) {

const $target = $content.find( '.targetClass' );

if ( $target.length ) {

// Do things with $target

}

// Only perform some operations when it is #mw-content-text in the argument

if ( $content.is( '#mw-content-text' ) ) {

const $note = $( '

' )

.addClass( 'myScript-note' )

.text( 'MyScript has successfully processed the content!' );

$content.prepend( $note );

}

} );

If your code works with page content and adds event handlers to DOM elements, then, instead of hooking to 'wikipage.content' and looking for elements to attach event listeners to when it is fired, you may attach one event listener to an element outside of the content area or the whole document but filter events by a selector (see [https://api.jquery.com/on/#on-events-selector-data-handler jQuery's documentation]). That is, instead of writing $content.find( '.targetClass' ).on( 'click', ... ) you can write $( document ).on( 'click', '.targetClass', ... ).

= Finding elements =

Every HTML element is a node in a DOM model allowing scripts to access the element, for example, on the following HTML page.

We can find element textarea:

  • Using its id: $( '#txtid' )
  • In the array of all elements with the same tag: $( 'textarea' )
  • Using an element next to it: $( '#neighbor' ).prev()
  • As a child of its parent: $( '#frmid' ).children( 'form' )
  • As a form element, using name: $( '#frmid [name="txtname"]' )

[http://jsfiddle.net/compwhizii/j2QRf/ This example on jsFiddle]

The [//api.jquery.com jQuery API reference] is an excellent source for documentation.

= Checking the current page =

Many scripts are supposed to work only on some pages. You can check:

  • The page type

if ( mw.config.get( 'wgAction' ) === 'history' ) { // Continue only on history pages.

if ( mw.config.get( 'wgCanonicalNamespace' ) === 'User_talk') { // Continue only on User_talk pages.

if ( mw.config.get( 'wgPageName' ) === 'Article_name' ) { // Continue only for the article "Article name".

  • Presence of elements (only in second and third parts of the script)

function func_start() {

if ( $( '#editForm' ).length == 0 ) return; //No edit form ? exit

// …

= Portlets (menus and tabs) =

{{main|Help:Customizing toolbars}}{{shortcut|WP:PORTLET}}

Portlets are MediaWiki's name for groups of links located in the topbar and sidebar. Here is a diagram of portlet ID's.

File:MediaWiki portlet names.svg portlets as seen in Vector legacy skin.]]

== List of portlets (portlet types)<span class="anchor" id="List of portlets"></span> ==

  • Top
  • p-personal - The links at the top right of the page. "personal" stands for "personal tools".
  • p-namespaces - The tabs on the left that never collapse. Not recommended, not much space. The article and talk tabs are located here.
  • p-views - The tabs in the middle that never collapse. Not recommended, not much space. The favorite page star tab is located here.
  • p-cactions - The items in the "More" tab's dropdown menu. "cactions" stands for "content actions".
  • p-search - Adding things here will mess up the appearance of the search box. Not recommended.
  • Left
  • p-logo - Adding things here will mess up the appearance of the logo. Not recommended.
  • p-navigation
  • p-interaction - Has the title "Contribute".
  • p-tb - Has the title "Tools". TB stands for toolbox.
  • p-coll-print_export - Has the title "Print/export". Not a good place to add things, since this should just be for printing and exporting.
  • p-wikibase-otherprojects - Has the title "In other projects". Not a good place to add things, since this should just be for links to other projects such as Wikisource, Wikibooks, etc.
  • p-lang - Has the title "Languages". Not a good place to add things, since this should just be for languages.

== Portlet structure ==

== Adding elements ==

There is a special function in mediawiki.util, [https://www.mediawiki.org/wiki/ResourceLoader/Core_modules#addPortletLink mw.util.addPortletLink()] that simplifies the process of adding your own links to portlets. The advantage of using this function is that your code should work across all skins, and not break when these skins change their HTML. Its parameters, in order:

  • portletId – ID of the target portlet
  • href – link URL
  • Set to '#' if you do not need to open a page and want to use a JavaScript listener instead.
  • text – human-readable link text
  • id (optional) – unique ID of the item
  • Use a prefix such as {{abbr|ca-|Content Actions (p-cactions)}}, {{abbr|pt-|Personal Tools (p-personal)}}, {{abbr|n-|Navigation (p-navigation)}}, or {{abbr|t-|Tools/Toolbox (p-tb)}} – for consistency with other links in the group of chosen portletId.
  • tooltip (optional) – helpful text appearing on mouse hover
  • accesskey (optional) – [https://www.w3schools.com/tags/att_global_accesskey.asp keyboard shortcut key]
  • Set to null if you do not need it.
  • Use $( '[accesskey=x]' ) in the console to see if 'x' is already used.
  • nextNode (optional) – element that this will be added in front of

// Several examples of portlet links

// Adds a link to your js file to the toolbox. tb = toolbox

mw.util.addPortletLink ( 'p-tb', mw.util.getUrl( 'Special:MyPage/common.js' ), 'My JS', 'pt-myvector', 'Visit your js file');

// Add a link to the edit page for your Notes in your personal links

// Note: We assume that short/pretty URLs are in use with ?action, ideally you would check for that.

mw.util.addPortletLink ( 'p-personal', mw.util.getUrl( 'Special:MyPage/Notes' ) + '?action=edit', 'My notes', 'pt-mynotes', 'Edit your personal notes' );

// Adds a link to prefix index for the current page to the toolbox

mw.util.addPortletLink ( 'p-tb', mw.util.getUrl( 'Special:Prefixindex/' + mw.config.get( 'wgPageName' ) ), 'Prefixindex', 'tb-prefixindex');

// Adds a link to logs for your account

mw.util.addPortletLink ( 'p-personal', mw.util.getUrl( 'Special:Log/' + mw.config.get( 'wgUserName' ) ), 'My logs', 'pt-mylogs');

Or you can use JQuery. Simply attach it in another place with .append(), .prepend(), .before(), or .after(). [https://www.w3schools.com/jquery/jquery_dom_add.asp][https://javascript.info/article/modifying-document/before-prepend-append-after.svg]. Warning: This is fragile. You may get it working on a couple skins, but a couple other skins may look broken.

// Add a clickable button on the edit article page, above the edit summary.

$('.editOptions').prepend('');

// Add a listener to your button, that does something when it is clicked.

$('#my-custom-button').click(function(e) {

// do things

});

= Removing elements =

To hide an element, you can use JQuery's [//api.jquery.com/hide/ .hide()] function.

// Example: remove special characters toolbar from edit page

$( '#editpage-specialchars' ).hide();

// Or modify the CSS directly

$( '#editpage-specialchars' ).css( 'display', 'none' );

Or you can do it by placing code in common.css:

  1. editpage-specialchars {

display:none;

}

= Adding menus =

You can add menus using mw.util.addPortlet() (see documentation). The menu will not show up until you put a portletLink in it. If you add a menu adjacent to #p-cactions, it will be a dropdown menu in the Vector and Vector 2022 skins, with the correct dropdown HTML added for you.

mw.util.addPortlet('p-twinkle', 'TW', '#p-cactions');

mw.util.addPortletLink('p-twinkle', '#', 'Tag');

mw.util.addPortletLink('p-twinkle', '#', 'CSD');

= Editing =

== Textarea with article wikicode ==

The most important element on the edit page is a