Written on 8/12/2014 in Web development

Javascript tooltips


CSS Javascript

The next thing on my task list is to add some readability to the posts. This is because, while writing, my mind tends to dwell off-subject from time to time and I want to share my thoughts. Often enough, this just isn't a good idea. When I read blog posts, I often see 'side notes' in parentheses (not a good idea)1. And while the parentheses are a good fit for these side notes, they still break the text flow. And text flow is amazingly important. I just have to say, I enjoy reading my own blog. Which is great, because that way, I'll never have 0 fans. And I like to think others enjoy reading it too. It's not the most technologically advanced blog because I don't enjoy scaring people, and it's not the best written blog either. But I try to make it entertaining, break out a few jokes now and then. It probably happens that it's too much and even too little. But I try to make it entertaining to read.

So I had the problem that I had thoughts or side notes that I wanted to share with you. I didn't want it to break the text flow by using parentheses and I wanted it to be easy to use, preferably making use of the markdown syntax. You can easily extend the markdown syntax, I generally do not deem it wise. Only because it's kept that simple for a reason. But with enough consideration, just go ahead!

Javascript as a UI enhancer

So enters Javascript. The allround magnificent working horse of the world wide web. Liked by many, hated by few. Using Javascript has always been an experience. Good and bad. I still believe it falls short of the OO languages in some aspects and while I enjoy writing Javascript, I enjoy writing C# more. Maybe it's because the IDE understands the language better or just the sense of safety you feel when the red wiggly lines disappear. But I just don't feel safe writing big chunks of Javascript code. Although my experiences with angular have been rather good, I would still prefer a strongly typed language on the backend.

I like to believe this working horse is called Javascript

Javascript does a great job at DOM manipulation. I have never felt more free in coding than I did while writing Javascript. I just adore making these little UI changes and making the page just that little bit more interactive. - That's not strange is it? Am I turning in a designer? Oh god no, don't let me change into a designer! - I'm not though. But I will attempt to solve the text flow problem using Javascript. Also, a real designer would probably be able to solve this better than I can.

In short, markdown supports links. The markdown syntax also allows adding a title attribute. So that when you hover over a link for 500ms, a nice little tooltip appears. Which is AMAZING. Tooltips are just the best. I hate that touch devices can't hover because tooltips can add a lot of information that you don't want the user to see if they aren't interested. The default tooltip doesn't do the best job. It appears slow, and disappears when you move your mouse, or even after a while.

So we use markdown for creating 'links' with title attributes set. We use 'href="#"' for adding a link without destination. I then use Javascript to find all links with the href attribute set to '#' and remove the href attribute3. Then, for tooltipping the links we use Javascript to select all links with a title attribute and do some CSS/Javascript magic. Another small update by Javascript is that when a link is set using a number as text, I will super this text. So you get these small '4' links that don't mess up the flow. The full code can be found below, it's commented and everything so you don't lose your way in my horrible Javascript coding style5.

(function setTooltipsAndFormatLinks() {
    // Get all anchors with a title attribute set within the post
    var tooltips = document.querySelectorAll('article > p a[title]');

    // Iterate them
    for (var index = 0; index < tooltips.length; ++index) {
        var tt = tooltips[index];
        // Event handling for showing / hiding tooltips (window.detections is a namespace I use for handling cross-browser stuff)
        window.detections.AddEventListener(tt, 'mouseover', showTooltip);
        window.detections.AddEventListener(tt, 'click', showTooltip); // For mobile devices?
        window.detections.AddEventListener(tt, 'mouseout', hideTooltip);

        // There is no markdown syntax for super-font, so we'll do that here dynamically on all links that are numbers
        if (/^\d+$/.test(tt.innerHTML)) {
            tt.innerHTML = '<sup>[' + tt.innerHTML + ']</sup>';
        }

        // When there is no link (href = #), then the link is only for tooltips -> remove href, set pointer to cursor
        if (tt.getAttribute('href') == '#' || tt.getAttribute('href') == '') {
            tt.removeAttribute('href');
            tt.style.cursor = 'default';
        }
    }

    function showTooltip(e) {
        var element = e != undefined ? e.srcElement || this : this; // Button that is clicked - there should be an easier way, please tell me there is an easier cross-browser way!

        // add a data-title attribute and remove the title attribute => if the title attribute is left, the default on hover title tooltip will still show and that's just plain ugly
        var text = element.getAttribute('title');
        element.removeAttribute('title');
        element.setAttribute('data-title', text);

        // Check whether the element already has a vorpaltooltip inside
        if (element.querySelectorAll('.vorpalTooltip').length == 0) {
            // element bounding rectangle (of the hyperlink that's being hovered)
            var rect = element.getBoundingClientRect();
            // container bounding rectangle => container is relatively positioned for *purposes*, 
            // so the absolute position will be determined by his rectangle instead of the window.
            var mainContainerRect = document.getElementById('MainContainer').getBoundingClientRect();
            var getWidth = function getWidth(container) { // Fallback for width (ie8 does not set width)
                return container.width || (container.right - container.left);
            };
            var getHeight = function getHeight(container) { // Fallback for height (ie8 does not set height)
                return container.height || (container.bottom - container.top);
            };

            // element bounding rectangle is calculated according to the relatively positioned main container
            // so we need to get the actual position relative to the main container and his position to the viewport
            var top = rect.top - mainContainerRect.top;

            // create the element and set the text
            var tt = document.createElement('div');
            tt.innerHTML = text;
            var left, right;

            // If the right bounding rectangle is in the first 75% region of the screen width, align tooltip right
            if (getWidth(mainContainerRect) * 0.75 > rect.right) {
                left = rect.right + 'px';
                right = 'auto';
            } else { // align tooltip left
                // If the height > normal(~16px), the anchor is multiline => tooltip middle for responsiveness purposes..
                if (rect.height > 30) {
                    left = 30;
                    right = 'auto';
                } else {
                    left = 'auto';
                    right = getWidth(mainContainerRect) - rect.left + 'px';
                }
            }


            tt.style.position = 'absolute';
            tt.style.left = left;
            tt.style.right = right;
            tt.style.top = top + getHeight(rect) + 'px';
            tt.className = tt.className + 'vorpalTooltip';

            element.appendChild(tt);
        } else { // tooltip was already enabled => anchor was clicked so remove tooltip (for mobile)
            hideTooltip();
            // reset the title attribute for SEO and accessibility purposes...
            element.setAttribute('title', element.getAttribute('data-title'));
        }
    }

    function hideTooltip(e) {
        // Get all anchors with a title attribute set within the post
        var tooltips = document.querySelectorAll('.vorpalTooltip');
        var element = e != undefined ? e.srcElement || this : this; // Button that is clicked

        // reset the title attribute for SEO and accessibility purposes...
        element.setAttribute('title', element.getAttribute('data-title'));

        // Iterate them and remove all tooltips (back-up plan for when even other tooltips are still being shown for whatever reason)
        for (var i = 0; i < tooltips.length; ++i) {
            tooltips[i].outerHTML = '';
            delete tooltips[i];
        }
    }
})();

It might be hard to believe, but before I was testing IE8, this code was rather clean, but it got cluttered while troubleshooting cross-browser. Because the tooltips are / look like links, I hope that an interested tablet user will also click the link. It would then either direct this user to the destination, which is what he would expect. When there is no href attribute, it shows the tooltip and he is still informed. I have not tested this yet. And it might just be that touch users hate me for this6 but I generally don't care2. See you next post!

Newer Older Top
blog comments powered by Disqus