Adding link icons to your anchor tags in SharePoint

ToolsAndResources

This is a simple trick that gives a bit of context to the end users of a SharePoint site. This technique is not new, and has been used by various web pages for a long time. It can be easily utilized in SharePoint context as well. In rich text like wiki pages and article pages you will often find a few links to different types of content. By default you don’t really know where the links will take you, unless you take a closer look at the browser’s preview of the URL on mouseover. It would be much easier to know where the links lead you (astray) if the links are being prepended by an icon, letting you recognize which content the links are pointing to.

Types of link icons

In my recent project we created this functionality for a few types of content, as you see from the example above. The types of content are internal link, external link, link to document, link to image, link to a user’s mysite profile and a mailto-link. We’ve also created a link icon for “the magnificent tool”. This is a selection of pages living in SharePoint within a specific site. Even though the links are to internal pages, we want them to have their own style, as “the magnificent tool” should be recognized beyond a normal internal link. Of course you could add as many or few types of link icons as you’d like.

How does it work?

I have defined a method Pzl.Core.AddLinkIconsToAnchorTags which accepts a jQuery selector for the anchor tags that should get link icons. This way the method can be reused on different kind of pages, for example wiki pages and article pages. The method is called when the DOM is ready, and it should also just be called when the page is in display mode, not in edit mode (or else it will insert into the markup of your content, which you really don’t want).

Pzl.Core.AddLinkIconsToAnchorTags('.pzl-wiki-page .page-content a, .pzl-wiki-page .right-wp-zone-col .ms-rtestate-field a');

The method itself looks like the following piece of javascript. As is apparent from the comments, a check is made for making sure the page is not in edit mode. A check is also made for each anchor tag in the selector, checking if it’s not a anchor tag in a SharePoint web part. This is because we don’t want to  apply link icons to out of the box webparts, because this will make things look really weird.

Pzl.Core.AddLinkIconsToAnchorTags = function (selector) { 
    //Don't apply custom link icons in edit mode. Will get messy 
    if (!Pzl.Core.IsEditMode()) { 
        //Filter out anchor tags that are not supposed to have links and anchor tags in webparts 
        jQuery(selector).not('.noUrlIcons,.ms-listlink').filter(':noparents(.ms-wpContentDivSpace,.ms-webpart-titleText)').each(function () { Pzl.Core.CheckLinkAndInsertIcon(this); }); 
    } 
};

Moving closer to where the magic happens, let’s take a look at the Pzl.Core.CheckLinkAndInsertIcon function, which is actually adding the icon to the link.

Pzl.Core.CheckLinkAndInsertIcon = function (anchorElement) {
    var anchor = jQuery(anchorElement);
    //Apply link icon if the anchor tag isn't already an image and doesn't contain an image
    if (!anchor.hasClass('pzl-icon-container') && anchor.has('img').length <= 0) {
        var url = anchor.attr('href');
        if (url != undefined && !url.startsWith('javascript')) {
            var linkObject = Pzl.Core.GetIconClassForLinkUrl(url);

            var linkIcon = "<span class='pzl-icon pzl-icon-" + linkObject.className + "' title='" + linkObject.toolTip + "'></span>";

            if (linkObject.external) anchor.attr('target', '_blank');

            anchor.addClass('pzl-icon-container inline-icon');
            anchor.prepend(linkIcon);
        }
    }
};

There are a few things to note here. First, pzl-icon-container is the css-class we’re adding to the anchor tag. We’re checking, just in case, that this class is not already present for the anchor element (we don’t want to add icons twice). We are also checking that the anchor doesn’t include an image, as we don’t want to add icons if we have image links – that would look bad. Moving on, we’re grabbing the actual href (url) of the anchor tag and checking that the url isn’t undefined or a javascript link.

Get to the point already!

Pzl.Core.GetIconClassForLinkUrl(url) is getting the link icon for that url, based on a set of rules. Basically it returns us an object with a className-property and a toolTip-property, as well as a property saying whether the link is external or not. The className-property is used for styling the icon, i.e. setting the right icon, and the toolTip-property is used as a tooltip for when the user is hovering the link icon.

We generate the span-markup for the link icon and add target=_blank to the anchor if it’s an external link. Finally, we’re adding the .pzl-icon-container and .inline-icon CSS-classes to the anchor tag, and prepending it with the link icon markup.

Deciding on the type of link

The logic for deciding on the type of link is a mix of regular expressions tests and indexOf-checks. I guess i could make it all regex tests. The checks are quite simple, and it’s easy to add new ones. As you see, the if’s higher up will “overrule” the if’s further down, meaning that e.g. a profile page link icon will take effect over an external url link icon and an external link icon will take effect over a document link icon.

Pzl.Core.GetIconClassForLinkUrl = function (url) {
    var toolTip = "Internal link";
    var className = "internal";
    var external = false;

    url = url.toLowerCase();
    var currentHost = location.protocol + '//' + location.hostname;
    if (url.indexOf('/person.aspx?accountname=') >= 0) {
        className = "profile";
        toolTip = "Profile page";
    }
    else if (/^(f|ht)tps?:\/\//i.test(url) && url.substring(0, currentHost.length) != currentHost) {
        className = "external";
        toolTip = "External link";
        external = true;
    }
    else if (/\.(gif|jpg|jpeg|tiff|png|bmp)$/i.test(url)) {
        className = "image";
        toolTip = "Image";
    }
    else if (/\.(doc|docx|xls|xlsx|pdf|odt|rtf|txt|ppt|pptx)$/i.test(url)) {
        className = "document";
        toolTip = "Document";
    }
    else if (/^(mailto:)/i.test(url)) {
        className = "mailto";
        toolTip = "E-mail";
    }
    else if (url.indexOf('/magnificent/') >= 0) {
        className = "magnificent";
        toolTip = "Magnificent tool";
    }
    var linkObject = {
        className: className,
        toolTip: toolTip,
        external: external
    };
    return linkObject;
};

CSS and making it look good

It would be pointless to post all the CSS for all the types of link icons we use here, but I’m going to share an example. We are using a sprite for icons, and hence all icons share background-image, and just use different positions. Here you’ll also see the reason why we’re using the .pzl-icon-container, to get a nice hover effect on the icons when the link is being hovered.

.pzl-icon {
    background-image: url('/_layouts/15/Pzl.Core/img/GFX-Sprite.png?rev=20140526');
    display: inline-block;
    width: 12px;
    height: 12px;
}
.pzl-icon-container.inline-icon .pzl-icon{
    padding-left: 3px;
    margin-bottom: -1px;
}
.pzl-icon-internal {
    background-position: 90px -90px;
    height: 13px;
}

    .pzl-icon-internal:hover {
        background-position: 60px -90px;
        height: 13px;
    }

.pzl-icon-container:hover .pzl-icon-internal {
    background-position: 60px -90px;
}

The boring part if you want to roll your own

You’ll have to create your own graphics sprite or handle the icons in your own way for this to work. It would be smoother if this worked without dependency on creating custom icons. The benefit of doing that though, is that you can make the icons with style and form consistent with the rest of your SharePoint site.

The final result

An example of how this is in play is seen in the following picture, here the user is hovering over the magnificent tool-link.

ToolsAndResourcesHover

Leave a Reply

Your email address will not be published. Required fields are marked *