0

I am trying to avoid a potential problem when I want to highlight all shortened links within a page. At the moment I have this code:

        $('a').each(function() {

            var urlHref = $(this).attr('href');
            var shortenedLinks = ["bit.ly", "amzn.to","goo.gl","t.co","lnkd.in"];

            for (var item in shortenedLinks) {
                if (urlHref.includes(shortenedLinks[item])) {
                    console.log('yes');
                } else {
                    console.log('no');
                }
            }
        });

This works fine as the result in the console is:

yes

(4) no

The potential Problem

At the moment, for every link it runs through the array and compares part of the URL. This is fine for a small amount of URLS however I have about 80 URLshortener services, and what if the page contained 80 links that would be 6,400 checks.

I tried this with a few links on the page and all the shortened url providers and it took a while to go through them all.

Question

Is there a way to speed up this process so I can check the array and part of the url without looping through?

Community
  • 1
  • 1
JamesG
  • 1,552
  • 8
  • 39
  • 86

2 Answers2

0

First some small tweaks, the break is the resource saver

// define the array more global, otherwise you set the array every call.
var shortenedLinks = ["bit.ly", "amzn.to","goo.gl","t.co","lnkd.in"];
$('a').each(function() {
    // use native JS where you can
    var urlHref = this.href // was: $(this).attr('href');


    for (var item in shortenedLinks) {
        // use starts with: https://stackoverflow.com/questions/646628/how-to-check-if-a-string-startswith-another-string
        if (urlHref.startsWith('http://'+shortenedLinks[item]) || urlHref.startsWith('https://'+shortenedLinks[item])) {
            console.log('yes');
            break; // we've found a match, stop looping <-- this is the biggest win
        } else {
            console.log('no');
        }
    }
});

You can improve this a little more by adding the http:// part in your array so you dont have to do them twice.


After that, you can also first check if it is an internal link. All internal links arent short urls, so you can skip those in one go, skipping the need to loop:

var shortenedLinks = ["bit.ly", "amzn.to","goo.gl","t.co","lnkd.in"];
function isHrefShorturl(elem){
    // <a href="/example"> is an internal link, continue
    if( elem.href.substr(0,1)!="/" ){
        for (var item in shortenedLinks) {
            // use starts with: https://stackoverflow.com/questions/646628/how-to-check-if-a-string-startswith-another-string
            if (elem.href.startsWith('http://'+shortenedLinks[item]) || elem.href.startsWith('https://'+shortenedLinks[item])) {
                return true; // when we find something, return true
            }
        }
    }
    return false; // fallback, return false if no matches
};

$('a').each(function() {
    if( isHrefShorturl(this) ){
        console.log('yes');
    } else {
        console.log('no');
    }
});
Martijn
  • 15,791
  • 4
  • 36
  • 68
  • 1
    Why was this downvoted - it looks like a good solution to me. Any comments as to why I shouldnt use this? – JamesG Aug 07 '17 at 11:53
  • My first round wasnt perfect (though not really downvote worthy (IMO)), maybe thats why. I've improved it anyway, used better names and added the internal link check :) – Martijn Aug 07 '17 at 11:57
  • Haha thanks. I've got some to spare. Though I appreciate it, just upvote when you think it upvote worthy :) – Martijn Aug 07 '17 at 12:00
  • @JamesG i dont know your real final target but if you just have to highlight the shortened links i'd go for the css-only solution – Lorenzo Catalano Aug 07 '17 at 13:33
0

you can directly get the anchors with href starting with your string like

$( "a[href^='http://bit.ly']" ).each //do something
$( "a[href^='http://amzn.to']" ).each //do something

but to check 80 domains you would have to query the dom 80 times,so you could join the selectors into one

$( "a[href^='http://bit.ly'],a[href^='http://amzn.to']" ).each //do something

If,as you post says,you are just trying to highlight those links maybe you can just use the same selector but in css

a[href^="http://bit.ly"],a[href^='http://amzn.to'] {
    background: #ffff00;
}

EDIT: after some discussions and testing,looks like the vanilla javascript approach is the fastest in terms of perfomance if the browsers supports querySelectorAll.

document.querySelectorAll('a[href^="http://bit.ly"],a[href^="http://amzn.to"],a[href^="http://goo.gl"]').forEach(function(e){
  e.style.background='#ffff00';//highlights the link
});

jquery's attribute selector is probably the slower on older browsers

still,the css option(again,browser dependent) would be the very best for performance and dynamically added links

  • I highly doubt this is better, the attribute selectors are expensive. At least to `$('a').filter("[href^='http://bit.ly']")` – Martijn Aug 07 '17 at 12:01
  • @Martijn, the attribute selector is much more performant in this case, which is a bit surprising. https://jsfiddle.net/daveSalomon/0u7ccesj/ – Dave Salomon Aug 07 '17 at 12:48
  • yeah, but the 'attribute selector' version has 2 options, and mine 5. You also have one external link (bit.ly) – Martijn Aug 07 '17 at 13:01
  • Though, when we add a lot more, it's still fast. https://jsfiddle.net/9f8hqg2p/1/ I have the feeling i'm missing something. – Martijn Aug 07 '17 at 13:07
  • eheh,weird :) what if a page has 1000 links(500 internal,250 shortened and 250 external not shortened),your solution would still check 1000 links,and cycle the array for a match 250 times for the shortened links and cycle the entire array an other 250 times without finding anything for the external non-shortened links – Lorenzo Catalano Aug 07 '17 at 13:26
  • i did some more test,i guess jquery attribute selector is slow when the browser does not support document.querySelectorAll,in fact i edited your fiddle(i lost it ._.) and using vanillajs with querySelectorAll was even faster,i'll update my answer – Lorenzo Catalano Aug 07 '17 at 14:14