Implementing feature rich tooltips in Oracle JET with custom Knockout bindings

Daniel Curtis
Oracle Developers
Published in
4 min readNov 13, 2017

There have been a couple of scenarios in which I have found tooltip examples and plugins not to be rich enough for a requirement, the Oracle JET cookbook is no exception to this.

The cookbook includes an example scenario which uses an element attribute to contain the tooltip content (I would imagine this suits the requirements for the majority of projects), but what if you wanted to do more… such as the example shown in the ojGant component:

ojGant component with tooltip template

In my case I needed to create a feature rich tooltip, and I wanted the implementation to be simple.

After some digging I decided the best approach would be to harness the power of creating custom knockout bindings, and this is a real showcase of the brilliance to JET. We have a framework that contains a lot of useful components to use, but if we ever needed to implement custom functionality, there is no need to be fighting the framework, it is relatively straight forward to extend.

So lets start by creating a custom binding (using the init callback, as in this case update is not needed), and bind it to a ojAvatar component.

(For this example I am going to use the ojAvatar from the cookbook)

viewModel

ko.bindingHandlers.toolTip = {
init: function (element, valueAccessor, allBindings) {

var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);

console.log("binding occurred! " + value)
}
}

view

<oj-avatar data-bind='css: value, attr:{"aria-label":"Avatar of " + $parent.firstName
+ " " + $parent.lastName + " with color " + label}, toolTip: "test"',
role='img' size='[[$parent.avatarSize]]' initials='[[$parent.initials]]'>
</oj-avatar>

This will write “binding occurred! test” to the log for every avatar in your array (6 different avatars). The value of ‘test’ is being passed through, and can be used a little later on in our popup logic.

Lets extend this further, and create a div which will hold some extra information about the user.

view

<!-- ko foreach: avatarColors -->
<div class="oj-flex-item oj-sm-margin-2x" data-bind="toolTip: true">
<oj-avatar data-bind='css: value, attr:{"aria-label":"Avatar of " + $parent.firstName + " " + $parent.lastName + " with color " + label}, ' role='img' size='[[$parent.avatarSize]]' initials='[[$parent.initials]]'>
</oj-avatar>
<div class="myTooltip" style="position:absolute; padding:10px; z-index:1; background-color: #ebebeb; left:-999999px;" data-bind="style: { border: '3px solid ' + color }">
<b>First name:</b> <span data-bind="text: $parent.firstName"></span><br>
<b>Surname:</b> <span data-bind="text: $parent.lastName"></span>
<oj-rating-gauge class="myRating" value="4" readonly style="width:90px;height:35px;" selected-state.color="{{color}}">
</oj-rating-gauge>
</div>

</div>
<!-- /ko -->

I have modified the code we started earlier, see the bolded code for changes.

Note: style attributes should be added to the myTooltip class, however the cookbook doesn’t enable editing of the stylesheets, so for the example I have included it inline.

Now that we have our view completed, lets modify the logic in the custom binding handler to hide / show our tooltip:

viewModel

ko.bindingHandlers.toolTip = {init: function (element, valueAccessor, allBindings) {   // Determine if popup should be loaded
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
if (valueUnwrapped == true) {
// Show or hide popup depedent on hover status
$(element).hover(function () {
$(element).find('.myTooltip').show();
}, function () {
$(element).find('.myTooltip').hide();
}).mousemove(function (e) {
// Enables the tooltip to follow the users cursor
var mousex = e.pageX + 50 //Get X coordinates
var mousey = e.pageY - 50 //Get Y coordinates
$(".myTooltip").css({ top: mousey, left: mousex });
})
}}}

The avatarColours array has also been extended to include the hex codes, which are used for the tooltip border and gauge star colour:

self.avatarColors = ko.observableArray([
{label: 'a', value: 'demo-avatar-a', color: '#0096e0'},
{label: 'b', value: 'demo-avatar-b', color: '#d97638'},
{label: 'c', value: 'demo-avatar-c', color: '#eb5a50'},
{label: 'd', value: 'demo-avatar-d', color: '#d16ba6'},
{label: 'e', value: 'demo-avatar-e', color: '#749e5a'},
{label: 'f', value: 'demo-avatar-f', color: '#c94e30'},
])
Custom tooltips using ojAvatar

The framework is in place to include custom data and components within your tooltip, enjoy!

— DC

Useful read: https://hackernoon.com/my-approach-to-using-z-index-eca67feb079c

--

--

Daniel Curtis
Oracle Developers

Lead Front End Developer at Griffiths Waite - JET, React, ADF & WCS. Author of Practical Oracle JET (http://www.practicaloraclejet.co.uk) & Oracle Ace