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.
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
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
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();
});
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
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.