Custom Exit Link Behavior

Custom Link Behavior

Hello Hitchhikers! This is a guide on how to add custom exit link behavior to an Answers experience.

Some Yext clients have a warning modal appear for external links on their website and would like to include this kind of notice on their Answers experience.

We can do this by intercepting click events on <a> elements (i.e. links). Then, if the link points to an external URL prevent the event’s default behavior (redirecting the page) and do something client specific.
To do that, first, jambo override the themes/answers-hitchhiker-theme/script/on-document-load.js partial. Code put in here is automatically run on page load. Next, in your script/on-document-load.js, you’ll want to add code that registers a urlHandler function, which is run for any click events on <a> tags.

function urlHandler(event, anchor) {
  // Handler goes here, specifics depend on use case
}

document.querySelector('body').addEventListener('click', function (event) {
  const anchor = event.target.closest('a');
  if (anchor !== null) {
    urlHandler(event, anchor)
  };
});

If you need the code to run in IE11, you will also need the following polyfill for Element.closest, which you can add at the top:

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.msMatchesSelector ||
    Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
  Element.prototype.closest = function(s) {
    var el = this;

    do {
      if (Element.prototype.matches.call(el, s)) return el;
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1);
    return null;
  };
}

Here’s a few example use cases:

Exit Links in an iFrame

For iFrame-d experiences that want to display a modal in the parent frame, one solution is to send a message to the parent frame for external links.

/**
 * Intercepts click events on <a> tags and posts a message to the parent frame.
 * 
 * @param {MouseEvent} event
 * @param {HTMLAnchorElement} anchor
 */
function urlHandler(event, anchor) {
  const isExternalLink = function(anchor) {
    let linkHostName = anchor.hostname.split('.').reverse();
    let domainHostName = window.location.hostname.split('.').reverse();
    return !anchor.href.match(/^(mailto|tel)\:/)
      && linkHostName[0] !== domainHostName[0]
      && linkHostName[1] !== domainHostName[1];
  }
  if (isExternalLink(anchor)) {
    event.preventDefault();
    window.parent.postMessage([{value:anchor.href}], '*');
  }
}

Above, we first need to determine whether the link points to an external site. Let’s say we have a link pointing to locations.chipotle.com, and we are currently on hitchhikers.yext.com. The code below says that, if the last two portions of the link’s hostname, chipotle and com, are the same as the current hostname, yext and com, then it is an internal link. For this case, since they’re different, locations.chipotle.com is an external link. We also check that the link does not have some URI scheme like mailto: or tel: link, which trigger email and phone prompts respectively.

In the case that this IS an external link, we prevent the default behavior of the event, which is a redirect, and instead post a message to the parent frame. This will need to be handled by some kind of listener for the message event on the parent frame. The exact postMessage call may need additional configuration depending on how the parent frame is set up.

Exit Links in a non-iFrame Experience

For exit links in a non-iFrame experience, the code will be very similar.

/**
 * Intercepts click events on <a> tags and then performs custom handling.
 * 
 * @param {MouseEvent} event
 * @param {HTMLAnchorElement} anchor
 */
function urlHandler(event, anchor) {
  const isExternalLink = function(anchor) {
    let linkHostName = anchor.hostname.split('.').reverse();
    let domainHostName = window.location.hostname.split('.').reverse();
    return !anchor.href.match(/^(mailto|tel)\:/)
      && linkHostName[0] !== domainHostName[0]
      && linkHostName[1] !== domainHostName[1];
  }
  if (isExternalLink(anchor)) {
    event.preventDefault();
    // Custom code to create/reveal a modal
  }
}

Note that, if the modal contains <a> tags, those will also be intercepted by the handler. This can be worked around by giving <a> tags in the modal specific data- attributes, which the urlHandler knows to not operate on, or to use alternative ways or redirecting, like a button with an onClick event.

Analytics

As a final example, you can perform analytics behavior without blocking the redirect at all. This is done by not calling event.preventDefault().

/**
 * Intercepts click events on <a> tags and then fires analytics before redirecting.
 * 
 * @param {MouseEvent} event
 * @param {HTMLAnchorElement} anchor
 */
function urlHandler(event, anchor) {
  const isExternalLink = function(anchor) {
    let linkHostName = anchor.hostname.split('.').reverse();
    let domainHostName = window.location.hostname.split('.').reverse();
    return !anchor.href.match(/^(mailto|tel)\:/)
      && linkHostName[0] !== domainHostName[0]
      && linkHostName[1] !== domainHostName[1];
  }
  if (isExternalLink(anchor)) {
    // Fire off analytics here
  }
}
1 Like