Load multiple files into a page

Hi Everyone. I’m working on a project that needs to extract links from external pages that are created with simplehtmldom and a cron job.

The problem I’m having is loading these files into my page using jQuery. The link’s ‘href’ seems to be incorrect and I can’t find a solution. It seems that with each link it add’s on a href from another link, then the next will add on 2 hrefs. Anyway, here’s the code:

<script type="text/javascript">

var domain1 = "http://domain1.com/";
var domain2 = "http://domain2.com/";
var domain3 = "http://domain3.com/";
var domain4 = "http://domain4.com/";
var domain5 = "http://domain5.com/";
$("document").ready(function()  {
	if ($(window).width() > 960) {
		$("#content ul li").load("link_extract1.html a",{},function(){
			$("a").each(function (i) {
				$(this).text($(this).text().substring(0,40));
				var  domain1_href = $(this).attr('href');
				var new_domain1_href = domain1 + domain1_href;
				$(this).attr('href',new_domain1_href);
			});
			$("a").append('...');    	
		});
		$("#content2 ul li").load("link_extract2.html a",{},function(){
			$("a").each(function (i) {
				$(this).text($(this).text().substring(0,40));
				var  domain2_href = $(this).attr('href');
				var new_domain2_href = domain2 + domain2_href;
				$(this).attr('href',new_domain2_href);
			});
			$("a").append('...');
		});
		$("#content3 ul li").load("link_extract3.html a",{},function(){
			$("a").each(function (i) {
				$(this).text($(this).text().substring(0,40));
				var  domain3_href = $(this).attr('href');
				var new_domain3_href = domain3 + domain3_href;
				$(this).attr('href',new_domain3_href);
			});
			$("a").append('...');
		});
		$("#content4 ul li").load("link_extract4.html a",{},function(){
			$("a").each(function (i) {
				$(this).text($(this).text().substring(0,40));
				var  domain4_href = $(this).attr('href');
				var new_domain4_href = domain4 + domain4_href;
				$(this).attr('href',new_domain4_href);
			});
			$("a").append('...');
		});
		$("#content5 ul li").load("link_extract5.html a",{},function(){
			$("a").each(function (i) {
				$(this).text($(this).text().substring(0,40));
				var  domain5_href = $(this).attr('href');
				var new_domain5_href = domain5 + domain5_href;
				$(this).attr('href',new_domain5_href);
			});
			$("a").append('...');
		});
	};
});

</script>

The href in each link_extract file is structured like so: <a href=“…/topic/4365747419”>Link Here</a> -> Which in turn I’d like to appear like <a href=“http://domain5.com/topic/4365747419”>Link Here</a>

So you can see how the href is loaded into the <a> for each item. But it appears to be tacking on links from the other files as well. I’m sure it’s a simple answer but I can’t see it. This is one of my first projects using jQuery.

I’d also like to know how I can only load the first 5 or so links from each external file.

Appreciate your time and help.

Hey Bo5ton

It looks like problem you’re having is that the “domain” URLs are being prepended to all links every time you go through one of the load sequences. There is an easy fix for it though, all we need to do is make sure that a context is passed in to the $(‘a’) selector so it doesn’t target all links on the page.

I’ll just give the first one as an example (with some comments):


$("#content ul li").load("link_extract1.html a", {}, function(){

    $context = $(this).closest("ul"); // our local context for this section

    $("a", $context).each(function() {  //pass the context as the second argument to the $() method

        var $this = $(this); // cache the object so we don't have to re-instantiate it several times
        var domain1_href = $this.attr('href');
        var new_domain1_href = domain1 + cleanRelativeLink( domain1_href ); //I wrote a function to clean up the relative URLs

        $this.text( $this.text().substring(0,40) + "..." ); //append your ellipsis here so we don't have to at the end
        $this.attr('href', new_domain1_href);

    });

});


/** Remove the relative parts of a href (i.e. the '../' part)
 * @param {String} relativeLinkString
 * @returns {String}
 */
function cleanRelativeLink ( relativeLinkString ) {
    return relativeLinkString.replace(/\\.\\.\\//g, '');
}


Since you’re effectively repeating a lot of things, I’m going write a follow up post on how we could make this application a little more DRY :slight_smile:

Ok, so here is where I’m at so far. I’ve created a “Syndication” class that has some internal methods to add configs and perform the transforms.

You can add “syndication” links with minimal config in and they will be loaded in to a specific div / ul / whatever on the page. It could still be better but so far avoids the whole copy/paste thing that was happening before and pushes toward a more configurable application.

Here goes, hopefully the comments will make enough sense out of it. Feel free to harass me if something isn’t clear :slight_smile:


function Syndication() {

    /**
     * Configurable values
     */
    this.config = {
        maxLinktextLength: 40,
        cutoffCharacter: "...",
        contextSelector: "ul"
    };


    /**
     * placeholder for link config
     */
    this.linkConfig = [];


    /** Add a syndication link to the config
     *
     *    @param {String} parentLink - a URL that will be prefixed on all links
     *    @param {String} linksFile - a URL to extract (relative) links from
     *    @param {String} element - a selector to load the elements in to
     *
     */
    this.addLinkConfig = function( parentLink, linksFile, element) {

        this.linkConfig.push({
            parentLink: parentLink,
            linksFile: linksFile,
            element: element
        });

    }


    /**
     * Run the Syndication
     */
    this.run = function() {
        var i, len;

        for ( i = 0, len = this.linkConfig.length; i < len; i++) {

            this.extractAndTransform( this.linkConfig[i] ) ;

        }

    }


    /**
     * Extract and transform the links for the current config item
     *
     * @param {Object} currentItem - The current linkConfig to be processed
     *
     */
    this.extractAndTransform = function( currentItem ) {
        var that = this;

        $( currentItem.element ).load( currentItem.linksFile + " a", {}, function(){

            // our local context for this section
            var $context = $(this).closest( that.config.contextSelector );

            //pass the context as the second argument to the $() method
            $("a", $context).each(function() {

                var $this = $(this),
                    //build the new link href
                    newHref = currentItem.parentLink + that.cleanRelativeLink( $this.attr('href') );

                //limit the length of the link text + add an (optional) cut
                $this.text( $this.text().substring(0, that.config.maxLinktextLength ) + that.config.cutoffCharacter );
                //apply new href
                $this.attr('href', newHref);

            });
        });
    }

    
    /**
     * Remove the relative parts of a href (i.e. the '../' part)
     *
     * @param {String} relativeLinkString
     * @returns {String}
     */
    this.cleanRelativeLink = function( relativeLinkString ) {
        return relativeLinkString.replace(/\\.\\.\\//g, '');
    }


}

var syndicate = new Syndication();

syndicate.addLinkConfig( 'http://example.com/posts/123/', 'links_1.html', '#content_1 ul li' );
syndicate.addLinkConfig( 'http://example.com/posts/456/', 'links_2.html', '#content_2 ul li' );
syndicate.addLinkConfig( 'http://example.com/posts/789/', 'links_3.html', '#content_3 ul li' );
syndicate.addLinkConfig( 'http://example.com/posts/1011/', 'links_4.html', '#content_4 ul li' );

$(document).ready(function(){

    syndicate.run();

});

Once again John you have come up with the goods! You’re an absolute champion! It works perfectly!

Thanks for your reply. I’m trying my best to learn all I can about Javascript & jQuery but it’s just so vast and has so much in it.

Do you recommend any books or learning tools for this kind of stuff?

Also, would it be possible to only return 5 or so links instead of everything?

It most certainly would be possible, and with quite minimal effort.

I added a property called maxLinks to the config



/**
 * Configurable values
 */
this.config = {
    maxLinktextLength: 40,
    cutoffCharacter: "...",
    contextSelector: "ul",
    maxLinks: 5 //max # of links to show, false to show all links
};


Then in the extractAndTransform method we simply remove the links after the index that we don’t want before we go through the each loop.


/**
 * Extract and transform the links for the current config item
 *
 * @param {Object} currentItem - The current linkConfig to be processed
 *
 */
this.extractAndTransform = function( currentItem ) {
    var that = this;

    $( currentItem.element ).load( currentItem.linksFile + " a", {}, function(){

        // our local context for this section
        var $context = $(this).closest( that.config.contextSelector ),
            counter = 0;

// ===> Select and remove all links with an index greater than our maxLinks property
        if (that.config.maxLinks !== false) {
            $("a:gt("+(that.config.maxLinks-1)+")", $context).remove();
        }

        //pass the context as the second argument to the $() method
        $("a", $context).each(function() {


            var $this = $(this),
                //build the new link href
                newHref = currentItem.parentLink + that.cleanRelativeLink( $this.attr('href') );

            //limit the length of the link text + add an (optional) cut
            $this.text( $this.text().substring(0, that.config.maxLinktextLength ) + that.config.cutoffCharacter );
            //apply new href
            $this.attr('href', newHref);

        });


    });
}

If you want to go back to showing more (or less, or all) links you can simply modify the maxLinks property


$(document).ready(function(){

    syndicate.config.maxLinks = 10; //show 10
    syndicate.config.maxLinks = false; //show all
    syndicate.config.maxLinks = 3; //show 3

    syndicate.run();

});



Excellent John. Thanks again!
I can’t say I expected it to be that complicated. But looking over it now I get the gist how it works.

I’m not sure where I went wrong on the original. But they way you set it out is much cleaner and efficient.

As I’m not very familiar with either, is this jQuery or straight JS?

Any book/tutorial recommendations?

In the original it was just a matter of the context in which you were addressing the links. You were basically looping through all links on the page every time a file was being loaded. (Which was in turn affecting the URL for all the links as you were effectively prepending all the URLs from your list to all the links on the page.

This code is a bit of a combination of jQuery and JS, but the pattern being used is pure JS. In it’s most simplistic form it’s a function that you create new instances of.


function MyClass () {
  this.foo = "It's Foo!";

  this.doSomething = function() {
    console.log( this.foo );
  }

}

bar = new MyClass();
bar.foo = "It's Bar!";

bar.doSomething(); // will log "It's Bar!" to the console

baz = new MyClass();
baz.doSomething(); // will log "It's Foo!" to the console

This pattern gives you the opportunity to create objects and even if they aren’t being used, it will give you a nice way to encapsulate your code.

For a slightly longer example see http://snipt.net/geekyjohn/basic-instantiable-object-example/

Some good books on JS that are probably worth reading: