Adjust tooltip behaviour.
authorChris Jaekl <cejaekl@yahoo.com>
Sat, 11 Nov 2017 06:44:57 +0000 (15:44 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Sat, 11 Nov 2017 06:44:57 +0000 (15:44 +0900)
Adds timeout before display, and centres the tip around the current mouse position.

app/index.html
app/lib.css
app/lib.js

index 8bd44c43c69f15ed8d14009d0bb63ab1626bcb27..61f182d854cdfbcf65a7427a28f791a09c37d35d 100644 (file)
@@ -21,7 +21,7 @@
       Showing <span id="first">0</span> through <span id="last">0</span> out of <span id="count">0</span> matching books.
     </div>
 
-    <div id="books">(No books found)</div>
+    <div id="books" onmousemove="onMouseMove();">(No books found)</div>
 
     <div id="details" class="tooltip" onclick="hideDetails();">(No information available)</div>
 
index 843adedd9643eafbbc48a5d8773a5e37d67e1900..97d68221f72547ae37dc9c3db37047f39de2ab2e 100644 (file)
@@ -83,7 +83,7 @@ div.tooltip {
   display: none;
   margin: 4px 0px 0px 0px;
   padding: 3px 3px 3px 3px;
-  position: fixed;
+  position: absolute;
   left: 50;
   text-decoration: none;
   top: 50;
index d59c350cb45264b8b1be9efb780d0160eacc5960..0f70f325b941ba78c1fde5af2f2fedeeeaf1f581 100644 (file)
@@ -4,18 +4,29 @@
 g_state = {
                cache: [],
                count: 0,
+               mousePos: {     // Last known position of the mouse cursor
+                       x: undefined,
+                       y: undefined
+               },
                first: 0,
                ids: [],
                last: (-1),
                map: {},        // map from book.Id to index into cache[]
-               pageSize: 9,
+               pageSize: 48,
                tooltip: {
                        bookId: undefined,
                        milliSecs: 500,     // time to wait before displaying tooltip
+                       mousePos: {
+                               x: undefined,
+                               y: undefined
+                       },
+                       threshold: 10,          // number of pixels that mouse can move before tip is dismissed
                        timer: undefined
                }
 }
 
+document.onmousemove = onMouseMove;
+
 function adjustPos(setting) {
        var value = parseInt(setting)
 
@@ -24,7 +35,7 @@ function adjustPos(setting) {
                return;
        }
 
-       var maxFirst = g_state.count - g_state.pageSize;
+       var maxFirst = Math.max(0, g_state.count - g_state.pageSize);
 
        if (value < 0) {
                g_state.first = 0;
@@ -56,7 +67,6 @@ function bookHtml(book) {
        }
        result     +=       '</a></td>'
                +       '<td onclick="displayDetails(' + book.Id + ');" '
-               +          ' onmouseout="stopTooltipTimer(); hideDetails();" '
                +          ' onmouseover="startTooltipTimer(' + book.Id + ');">'
                +         '<p><b>' + book.Title + '</b></p>'
                +         '<p>'
@@ -113,17 +123,55 @@ function constructSearchUrl() {
 
 // Set the book ID for the details pane, and then show it
 function displayDetails(bookId) {
-       console.log('displayDetails()', bookId);
        g_state.tooltip.bookId = bookId;
        showDetails();
 }
 
 function hideDetails() {
+       g_state.tooltip.mousePos.x = undefined;
+       g_state.tooltip.mousePos.y = undefined;
+       
        var elem = document.getElementById('details');
-       elem.innerHtml = '';
+       elem.innerHTML = '';
        elem.style.display = 'none';
 }
 
+function onMouseMove(event) {
+       if (typeof event === 'undefined') {
+               return;
+       }
+       
+       var x = event.pageX;
+       var y = event.pageY;
+       
+       if (  x === g_state.mousePos.x
+          && y === g_state.mousePos.y)
+       {
+               // No change from previous known position. 
+               // Nothing to see (or do) here, move along.
+               return;
+       }
+       
+       // Remember current mouse (x,y) position
+       g_state.mousePos.x = x;
+       g_state.mousePos.y = y;
+
+       // Is there an active tooltip?
+       if (typeof g_state.tooltip.mousePos.x === 'undefined') {
+               // No active tooltip, so nothing further to do
+               return;
+       }
+       
+       var deltaX = Math.abs(x - g_state.tooltip.mousePos.x);
+       var deltaY = Math.abs(y - g_state.tooltip.mousePos.y);
+       
+       if (  deltaX > g_state.tooltip.threshold
+          || deltaY > g_state.tooltip.threshold )
+       {
+               hideDetails();
+       }
+}
+
 function onNext() {
        if (g_state.last < (g_state.count - 1)) {
                adjustPos(g_state.first + g_state.pageSize);
@@ -141,17 +189,12 @@ function onSlide(value) {
 }
 
 function onSearch() {
-       console.log('onSearch()');
-
        var url = constructSearchUrl();
 
-       report('Loading data from server, please wait...')
-       console.log('Fetching:  "' + url + '"...')
-
        fetch(url, {method:'GET', cache:'default'})
        .then(response => response.json())
        .then((jsonValue) => {
-               console.log('JSON response:  ', jsonValue);
+               // console.log('JSON response:  ', jsonValue);
                g_state.ids = jsonValue
                g_state.count = g_state.ids.length;
                g_state.first = (-1)
@@ -169,8 +212,6 @@ function onSearch() {
 }
 
 function refreshData() {
-       report('Loading details for books ' + g_state.first + ' through ' + g_state.last + ', please wait...');
-
        var i;
        var url = '/info/?ids=';
        g_state.map = {};
@@ -187,7 +228,6 @@ function refreshData() {
        .then(response => response.json())
        .then((jsonValue) => {
                console.log('JSON response for info:  ', jsonValue);
-               report('');
                g_state.cache = jsonValue;
                refreshLayout();
        })
@@ -218,8 +258,6 @@ function report(message) {
 }
 
 function showDetails() {
-       console.log('showDetails()', g_state.tooltip.bookId);
-       
        var id = g_state.tooltip.bookId;
        var elem = document.getElementById('details');
        var index = g_state.map[id];
@@ -229,13 +267,33 @@ function showDetails() {
        + ce(book.Description)
        + '</div>';
 
+       // Remember the current mouse (x,y).
+       // If we move the mouse too far from this point, that will trigger hiding the tooltip.
+       g_state.tooltip.mousePos.x = g_state.mousePos.x;
+       g_state.tooltip.mousePos.y = g_state.mousePos.y;
+
        elem.innerHTML = html;
-       elem.style.display = 'block';
+
+       elem.style.display = 'block';   // show, and calculate size, so that we can query it below
+       
+       var x = g_state.mousePos.x;
+       var y = g_state.mousePos.y;
+       
+       var bcr = elem.getBoundingClientRect();
+       
+       var width = bcr.width;
+       var height = bcr.height;
+       
+       x = Math.max(x - (width / 2), 0);
+       y = Math.max(y - (height / 2), 0);
+       
+       elem.style.left = x + 'px';
+       elem.style.top = y + 'px';
 }
 
 function startTooltipTimer(bookId) {
        if (typeof g_state.tooltip.timer !== 'undefined') {
-               clearTimer(g_state.tooltip.timer);
+               clearTimeout(g_state.tooltip.timer);
        }
        g_state.tooltip.bookId = bookId;
        g_state.tooltip.timer = setTimeout(showDetails, g_state.tooltip.milliSecs);
@@ -245,6 +303,7 @@ function stopTooltipTimer() {
        if (typeof g_state.tooltip.timer === 'undefined') {
                return;
        }
+       
        clearTimeout(g_state.tooltip.timer);
        g_state.tooltip.timer = undefined;
        hideDetails();