On page load, this script will locate all external links, and set their on click to open the page in a new window. "External links" includes pages that are on a different domain, PDF's on any domain, and any links with the class open-new
. When a PDF is clicked, it also pings Google Analytics.
This is using only pure Javascript, no jQuery or any other libraries. A previous version did use jQuery, but the dependency was too heavy for the benefit, so no recommendations to use jQuery, please.
This is not intended to be a library to be used generally, but is for a specific purpose, so "excess specificity" is acceptable.
It is designed to work cross-browser, so IE9+, FF, Chrome, etc.
// Find all external links, and set them to open in a new window
"use strict";
function isNull(obj) {
return typeof obj === "undefined" || obj === null;
}
function isNullOrEmptyString(obj) {
return isNull(obj) || obj === "";
}
function Linkerator(doc, url) {
this.document = doc;
var domain = url.substring(url.indexOf(":"), url.indexOf("/", 8));
this.URL_BASE = "http" + domain;
this.SSL_BASE = "https" + domain;
}
Linkerator.Link = function(aElement, urlBase, sslBase) {
this.aElement = aElement;
this.URL_BASE = urlBase;
this.SSL_BASE = sslBase;
};
Linkerator.Link.prototype = {
"getHref": function() {
return this.aElement.getAttribute("href");
},
"isHrefEmpty": function() {
return isNullOrEmptyString(this.getHref());
},
"isExternal": function() {
return !this.isLocal();
},
"isLocal": function() {
return !this.hasClass("open-new") &&
!this.isPDF() &&
this.hasLocalPath();
},
"hasClass": function(wantedClass) {
var classes = this.aElement.getAttribute("class");
if(isNullOrEmptyString(classes)) {
return false;
}
classes = classes.split(/\s+/);
return classes.some(function(className) {
return className === wantedClass;
});
},
"isPDF": function() {
return this.getHref().match(/\.pdf/i);
},
"hasLocalPath": function() {
var href = this.getHref();
return href.match(/^(\.\.?)?\//) ||
href.match(this.URL_BASE) ||
href.match(this.SSL_BASE);
},
"onClick": function(action) {
var thisLink = this;
var wrappedAction = function(evt) {
return action.call(thisLink, evt, thisLink);
};
if(this.aElement.addEventListener) {
this.aElement.addEventListener("click", wrappedAction, false);
} else if(this.aElement.attachEvent) {
this.aElement.attachEvent("onclick", wrappedAction, false);
} else {
this.aElement.onclick = action;
}
}
};
Linkerator.prototype.getExternalLinks = function() {
var linksArr = [];
var linksQS = this.document.querySelectorAll("#content a[href]:not([href^='#']):not([href^='javascript:']), a[href].open-new");
for(var i = 0; i < linksQS.length; i++) {
var link = new Linkerator.Link(linksQS[i], this.URL_BASE, this.SSL_BASE);
if(!link.isHrefEmpty() && !link.isLocal()) {
linksArr.push(link);
}
}
return linksArr;
};
function documentReady() {
var linkerator = new Linkerator(document, window.location.href);
linkerator.getExternalLinks().forEach(function(link) {
link.onClick(function(evt) {
if(evt.preventDefault) {
evt.preventDefault();
}
var href = this.getHref();
window.open(href);
if(typeof ga === "function" && this.isPDF()) {
ga("send", "event", "pdf", "click", href, {"hitCallback": function() {}});
}
return false;
});
});
}
if(document.addEventListener) {
document.addEventListener('DOMContentLoaded', documentReady);
} else if(document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if(document.readyState === "complete") {
documentReady();
}
});
} else {
window.onload = documentReady;
}