In this tutorial we'll walk through creating a simple JavaScript ToDo app with the OOUI library, which was created by the Wikimedia Foundation. OOUI has a lot of potential for super-powerful JavaScript applications in your browser — so we will start small and grow as we go, hopefully giving you a taste of the library and its concepts.
After installing OOUI in our project directory, we will start our project by creating these three files:
assets/
and inside it create a file and name it init.js
.
This will be our initialization script.
todo.css
and place it in your main
project directory—we will use this file later for all of our custom CSS styling.
index.html
and place it in your main
project directory. This is the first file we'll be populating with code.We will now attach the CSS and JavaScript files, along with the OOUI files, to our HTML page. This is how our basic page should look:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>ToDo OOUI</title>
<meta name="description" content="A demo ToDo app made with OOUI">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- jQuery -->
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<!-- OOjs -->
<script src="node_modules/oojs/dist/oojs.min.js"></script>
<!-- OOUI -->
<script src="node_modules/oojs-ui/dist/oojs-ui.min.js"></script>
<!-- OOUI theme -->
<script src="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.js"></script>
<link rel="stylesheet" href="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.css">
<!-- ToDo app custom -->
<link rel="stylesheet" href="todo.css">
<script src="assets/init.js"></script>
</head>
<body>
<div class="wrapper">
<h1>Demo ToDo app with OOUI</h1>
</div>
</body>
</html>
We will use the wrapper
div element to inject our
application into.
So now that we have our basic page, we need to start writing code. Our ToDo app should have two main pieces to start with: An input to add a new item, and a list displaying all items that have been added.
For the input We will need to use an OO.ui.TextInputWidget
.
And since a ToDo list allows us to show a list of items that can be selected, for the list
itself we will use an OO.ui.SelectWidget
(You can see a demo of all OOUI widgets in
the demo page).
Here is how you can add a SelectWidget
and an
TextInputWidget
to your assets/init.js
file:
$( function () {
const input = new OO.ui.TextInputWidget(),
list = new OO.ui.SelectWidget();
// Append to the wrapper
$( '.wrapper' ).append(
input.$element,
list.$element
);
} );
Let's break this up and see what we did there.
One of OOUI's principles is to separate the data from the UI, so each one of the
widgets we're creating is first and foremost an object that is separate from the DOM.
That object contains the DOM element itself in the $element
property, which we use to
attach to the document, but the behavior itself (as we will soon see) is done through
the general OOUI object.
So in short, we created two widgets—a text input and a select widget—and then
attached their $element
to the document. If
you load your page, it should have the title and an input. The list is invisible
because we don't have a way to add elements to it yet—so let's do that now.
We have our input, and we have the list, and now we need to connect them.
OO.ui.TextInputWidget
emits several events. One of them is simply "enter" when the Enter key is pressed (You can see all events
in the documentation). Let's make our input add an item to the list when we hit
the "enter" key.
Since the list is an OO.ui.SelectWidget
we should
add into it an OO.ui.OptionWidget
.
// Respond to 'enter' keypress
input.on( 'enter', function () {
// Add the item
list.addItems( [
new OO.ui.OptionWidget( {
data: input.getValue(),
label: input.getValue()
} )
] );
} );
That would add an item to the list. Try it out! Add a ToDo item and click Enter in the demo box below:
You can make your ToDo list items change color to indicate an item has been hovered or selected.
We'll achieve this effect by overriding existing selectors. Add the following code to your
todo.css
file:
.oo-ui-selectWidget-unpressed .oo-ui-optionWidget-selected {
background-color: #80ccff;
}
.oo-ui-optionWidget-highlighted {
background-color: #b9e3ff;
}
Now try it out:
But what happens if we try to add an item that already exists in the list? Let's add a condition that checks whether the item exists first before it is added. Let's also prevent adding an empty input.
Update your assets/init.js
file to look like this:
// Respond to 'enter' keypress
input.on( 'enter', function () {
// Check for duplicates and prevent empty input
if ( list.findItemFromData( input.getValue() ) ||
input.getValue() === '' ) {
input.$element.addClass( 'todo-error' );
return;
}
input.$element.removeClass( 'todo-error' );
// Add the item
list.addItems( [
new OO.ui.OptionWidget( {
data: input.getValue(),
label: input.getValue()
} )
] );
} );
Now we can only add unique items to this list. When an illegal value is added
(an empty input or an existing item), we attach the class
.todo-error
to the input.
For it to actually show something, we need to define it in our CSS
file. Add this to your todo.css
file:
.todo-error input {
background-color: #ff9696;
}
Let's try out a live demo of this version, and see if it behaves as expected:
It works! Now, let's add a little bit of extra flair to the app.
Let's make sure that our list and our input are styled a bit better, and add a placeholder
test to the input. Let's go back to the piece in our init.js
file where we created the widgets, and add configuration to both:
$( function () {
const input = new OO.ui.TextInputWidget( {
placeholder: 'Add a ToDo item'
} ),
list = new OO.ui.SelectWidget( {
classes: [ 'todo-list' ]
} );
// code continues...
} );
The above configuration adds a CSS class to the list widget and a placeholder to the text input widget.
In the part of our init.js
where we've added items
to the list, we can use input.setValue( '' );
to
clear the input each time an item is added.
// ...code
list.addItems( [
new OO.ui.OptionWidget( {
data: input.getValue(),
label: input.getValue()
} )
] );
input.setValue( '' );
} );
We can now go edit our todo.css
stylesheet. Notice that
we can also style the underlying objects, which (for now) we will do by calling their
oo-ui-style class, similarly to what we did in the Highlights
section.
.wrapper {
width: 60%;
margin-left: auto;
margin-right: auto;
}
.todo-list .oo-ui-optionWidget {
border-bottom: 1px solid #666;
}
One last thing: let's add in some padding to our CSS.
.oo-ui-inputWidget {
margin-bottom: 0.5em;
}
.todo-list .oo-ui-labelElement-label {
margin-left: 0.25em;
}
.oo-ui-labelElement .oo-ui-optionWidget {
padding: 0.25em;
}
That's it. We now have a basic ToDo app. Yay!
Reload your own app and look at your wonderful result. Here is our final demo:
For your convenience, we included the complete code below.
Here's the full code of our assets/init.js
file:
$( function () {
const input = new OO.ui.TextInputWidget( {
placeholder: 'Add a ToDo item'
} ),
list = new OO.ui.SelectWidget( {
classes: [ 'todo-list' ]
} );
// Respond to 'enter' keypress
input.on( 'enter', function () {
// Check for duplicates and prevent empty input
if ( list.findItemFromData( input.getValue() ) ||
input.getValue() === '' ) {
input.$element.addClass( 'todo-error' );
return;
}
input.$element.removeClass( 'todo-error' );
// Add the item
list.addItems( [
new OO.ui.OptionWidget( {
data: input.getValue(),
label: input.getValue()
} )
] );
input.setValue( '' );
} );
// Append the app widgets
$( '.wrapper' ).append(
input.$element,
list.$element
);
} );
Here's the full code of our index.html
file:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>ToDo OOUI</title>
<meta name="description" content="A demo ToDo app made with OOUI">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- jQuery -->
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<!-- OOjs -->
<script src="node_modules/oojs/dist/oojs.min.js"></script>
<!-- OOUI -->
<script src="node_modules/oojs-ui/dist/oojs-ui.min.js"></script>
<!-- OOUI theme -->
<script src="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.js"></script>
<link rel="stylesheet" href="node_modules/oojs-ui/dist/oojs-ui-wikimediaui.min.css">
<!-- ToDo app custom -->
<link rel="stylesheet" href="todo.css">
<script src="assets/init.js"></script>
</head>
<body>
<div class="wrapper">
<h1>Demo ToDo app with OOUI</h1>
</div>
</body>
</html>
Here's the full code of our todo.css
file:
.todo-error input {
background-color: #ff9696;
}
.wrapper {
width: 60%;
margin-left: auto;
margin-right: auto;
}
.todo-list .oo-ui-optionWidget {
border-bottom: 1px solid #666;
}
.oo-ui-selectWidget-unpressed .oo-ui-optionWidget-selected {
background-color: #80ccff;
}
.oo-ui-optionWidget-highlighted {
background-color: #b9e3ff;
}
.oo-ui-inputWidget {
margin-bottom: 0.5em;
}
.todo-list .oo-ui-labelElement-label {
margin-left: 0.25em;
}
.oo-ui-labelElement .oo-ui-optionWidget {
padding: 0.25em;
}