From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
In this tutorial we are going to create a blogroll slider that shows the latest post of your favorite blogs. We will be using jQuery, PHP and XSL. The aim is to get a given RSS feed of a blog and parse the XML data with the help of PHP. We will apply XSL to transform the feed into HTML and cache the result for subsequent use. We will be using a lot of CSS3 properties for a polished look.
So, let’s start!
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The Markup
The HTML is going to consist of two major parts: the list of blogs and the overlay modal window. Let’s take a look at the list of blogs:
<div class="content"> <h2>Latest Post Blogroll Slider</h2> <ul id="friendsList" class="friendsList"> <li class="ourtuts"> <a href="#"> <em>Our Tuts</em> <span>The Easy Way to Learn Photoshop</span> </a> </li> <li class="devisefunction"> <a href="#"> <em>Devise Function</em> <span>Tutorials and Resources</span> </a> </li> ... </ul> </div>
The unordered list is going to be wrapped by a div. For each list element we will have a specific class since we want to add a little icon that identifies the blog listed. We will also add a span for some blog description.
The overlay and the modal box will have the following structure:
<div id="overlay" class="overlay" style="display:none;"></div> <div id="modal" class="modal" style="display:none;"> <a href="#" class="prev"></a> <a href="#" class="next"></a> <span class="close"></span> <h2 id="blog_info"></h2> <div id="latest_post" class="loading"></div> </div>
We will give both elements the inline style of display:none because we don’t want them to show up in the beginning. You can also define that style in the CSS and not as inline style. In our jQuery function we will show the elements if we click on one of the blogs.
In the div with id latest_post we will insert the feed content.
Let’s take a look at the styling.
The CSS
First, we will define some resetting and general style:
*{ margin:0; padding:0; } body{ font-family:"Myriad Pro",Arial, Helvetica, sans-serif; background:#282828 url(../images/bg.jpg) repeat top left; overflow-x:hidden; }
We need to set the body overflow-x:hidden because we don’t want the scroll bar to appear when we slide out our modal box. If this is not convenient for your integration, just leave it out.
We will start by styling the elements that belong to the overlay look first.
The black semi-transparent overlay will have the following style:
.overlay{ position:fixed; z-index:999; top:0px; left:0px; width:100%; height:100%; background-color:#000; opacity:0.7; }
We will fix the overlay and stretch it over the whole window. If the user needs to scroll the screen, the overlay will stay in place like that.
The modal window will be absolutely positioned:
.modal{ position:absolute; top:25%; width:500px; height:300px; margin-left:-250px; z-index:9999; color:#fff; text-shadow:1px 1px 1px #000; border:1px solid #303030; background-color:#212121; -moz-box-shadow:0px 0px 10px #000; -webkit-box-shadow:0px 0px 10px #000; box-shadow:0px 0px 10px #000; }
In our jQuery function we will set the left value of the box to 50% (when it slides in), and in order to center it we need to give it a negative margin of half its width.
Notice the z-indexes of the overlay and the modal window: first, it’s the overlay with a value of 999 (very high in order to be on top of everything else in the page) and then the modal window with a z-index value of 9999. Of course, you could simply set it 1000 and it would be on top of the overlay. I like to leave some space since you never know if you need to add an element in between.
The headings and the paragraph are going to have the following style:
.modal h2{ font-weight:100; padding:5px 0px 5px 5px; margin:5px; background-color:#111; border:1px solid #272727; } .modal h2 img{ float:left; width:28px; margin-right:10px; -moz-box-shadow:0px 0px 4px #000; -webkit-box-shadow:0px 0px 4px #000; box-shadow:0px 0px 4px #000; } .modal h3{ text-transform:uppercase; font-weight:100; font-size:16px; letter-spacing:1px; margin:25px 25px 5px 25px; color:#3365AF; } .modal p{ margin:15px 25px; line-height:24px; height:100px; overflow:hidden; } .modal p a{ color:#ddd; } .modal p a:hover{ color:#fff; }
The tiny line under the title is created by giving the following style to the div:
.line{ background-color:#000; border-bottom:1px solid #333; height:1px; margin:3px 10px 5px 10px; }
We simply give the div a height of 1px and color it black. Then we add a bottom border with a lighter color and we have a nice reflected line look. You simply need to adapt the colors to your current background colors.
The button is pure CSS3 goodness:
a.button{ display: inline-block; float:right; padding: 4px 10px; background-color: #1951A5; color:#fff; margin:20px; font-size:12px; letter-spacing:1px; text-shadow:1px 1px 1px #011c44; text-transform:uppercase; text-decoration: none; border:1px solid #4c7ecb; outline:none; background-image: -moz-linear-gradient( top, rgba(255,255,255,0.25), rgba(255,255,255,0.05) ); background-image: -webkit-gradient( linear, left top, left bottom, color-stop(0, rgba(255,255,255,0.25)), color-stop(1, rgba(255,255,255,0.05)) ); -moz-box-shadow: 1px 1px 3px #000; -webkit-box-shadow: 1px 1px 3px #000; box-shadow: 1px 1px 3px #000; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } a.button:hover{ color: #011c44; text-shadow: 1px 1px 1px #ccdffc; } a.button:active{ margin-top:21px; }
We use a beautiful blue background gradient and a slick engraved hover effect by setting the fitting text colors and shadows.
The style of the wrapper div of the post when it’s loading is going to be the following:
.loading{ background:transparent url(../images/loading.gif) no-repeat center center; width:100%; height:200px; }
We will add and remove this class according to the status of the content.
The navigation links will have the following common style:
a.next, a.prev{ height:50px; width:20px; background-color:#212121; background-repeat:no-repeat; background-position:center center; position:absolute; top:130px; cursor:pointer; border:1px solid #303030; outline:none; }
… and the following single style:
a.next{ right:-21px; border-left:none; background-image:url(../images/next.png); -moz-border-radius:0px 5px 5px 0px; -webkit-border-top-right-radius:5px; -webkit-border-bottom-right-radius:5px; border-top-right-radius:5px; border-bottom-right-radius:5px; -moz-box-shadow: 1px 1px 3px #000; -webkit-box-shadow: 1px 1px 3px #000; box-shadow: 1px 1px 3px #000; } a.prev{ left:-21px; border-right:none; background-image:url(../images/prev.png); -moz-border-radius:5px 0px 0px 5px; -webkit-border-top-left-radius:5x; -webkit-border-bottom-left-radius:5px; border-top-left-radius:5px; border-bottom-left-radius:5px; -moz-box-shadow: -1px 1px 3px #000; -webkit-box-shadow: -1px 1px 3px #000; box-shadow: -1px 1px 3px #000; }
The span for closing the modal window is going to be styled as follows:
span.close{ background:#000 url(../images/close.png) no-repeat center center; cursor:pointer; height:25px; width:25px; position:absolute; right:12px; top:12px; cursor:pointer; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; opacity:0.5; } span.close:hover{ opacity:1.0; }
Hovering any element like a span will only work in modern browsers.
Now, let’s take a look at the content and the blog list:
.content{ width:800px; margin:40px auto 0 auto; background-color:#101010; height:555px; border:1px solid #191919; -moz-box-shadow:0px 0px 10px #000 inset; -webkit-box-shadow:0px 0px 10px #000 inset; box-shadow:0px 0px 10px #000 inset; }
The border color and the inset box shadow will create a really nice depth effect.
.content h2{ background-color:#222; color:#f0f0f0; text-shadow:1px 1px 1px #000; margin:-15px -15px 0 -15px; padding:20px; font-size:30px; font-weight:100; border:1px solid #303030; }
By using negative margins, we can pull the h2 heading out of the content box.
The blog list will have the following style:
ul.friendsList{ list-style:none; margin:10px 0 0 0; } ul.friendsList li a{ display:block; padding:15px 15px 15px 55px; text-decoration:none; color:#ddd; font-size:26px; text-shadow:1px 1px 1px #000; margin:5px 10px; background-color:#1f1f1f; border:1px solid #222; -moz-box-shadow:0px 0px 10px #000; -webkit-box-shadow:0px 0px 10px #000; box-shadow:0px 0px 10px #000; background-repeat:no-repeat; background-position:5px 50%; opacity:0.9; outline:none; } ul.friendsList li a:hover{ color:#fff; border:1px solid #303030; background-color:#212121; opacity:1.0; text-shadow:0px 0px 1px #fff; }
The specific image for each blog will be defined by their own class for the list element:
ul.friendsList li.devisefunction a{ background-image:url(../images/devisefunction.jpg) } ul.friendsList li.wordrom a{ background-image:url(../images/wordrom.jpg) } ul.friendsList li.fearlessflyer a{ background-image:url(../images/fearlessflyer.jpg) } ul.friendsList li.bluefaqs a{ background-image:url(../images/bluefaqs.jpg) } ul.friendsList li.ourtuts a{ background-image:url(../images/ourtuts.jpg) } ul.friendsList li.tzine a{ background-image:url(../images/tzine.jpg) } ul.friendsList li.w3avenue a{ background-image:url(../images/w3avenue.jpg) }
The description of the blog will be styled as follows:
ul.friendsList li a > span{ color:#333; padding-left:10px; font-size:22px; } ul.friendsList li a:hover > span{ text-shadow:1px 1px 1px #000; color:#444; }
And that’s all the style! Let’s take a look at the PHP.
The PHP
Our PHP class is going to take care of fetching the RSS Feeds and caching them after a transformation. A folder called “cache” is going to be created on disk and our PHP class is going to read from those files (if they were created) until a defined time limit is reached (cache_lifetime).
class RSS{ /** * the cache directory where we are saving our feeds output */ private $cache_dir = 'cache'; /** * time in seconds that the feed will be cached */ private $cache_lifetime = 300; /** * the variable passed with AJAX, * that holds the name of the blog we want to fetch the RSS */ private $source; /** * the xsl stylesheet that will * transform the rss feed into html */ private $xsl_file = 'rss2html.xsl'; public function __construct($source){ $this->source = $source; } /** * gets the latest post of the respective blog */ public function getLatest(){ switch($this->source){ case 'wordrom': $xml = 'http://feeds.feedburner.com/Wordrom?format=xml'; break; case 'w3avenue': $xml = 'http://feeds.feedburner.com/w3avenue?format=xml'; break; case 'fearlessflyer': $xml = 'http://feeds.feedburner.com/fearlessflyer?format=xml'; break; case 'tzine': $xml = 'http://feeds.feedburner.com/Tutorialzine?format=xml'; break; case 'bluefaqs': $xml = 'http://feeds.feedburner.com/Bluefaqscom?format=xml'; break; case 'devisefunction': $xml = 'http://feeds.feedburner.com/devisefunction?format=xml'; break; case 'ourtuts': $xml = 'http://feeds.feedburner.com/ourtutsfeed?format=xml'; break; } /** * if we have the output in the cache, * we fetch that one. * otherwise we load it and save it in the cache */ if($this->isCached($this->source . '.xml')){ echo $this->getCache($this->source . '.xml'); } else{ $doc = new DOMDocument(); $xsl = new XSLTProcessor(); $doc->load($this->xsl_file); $xsl->importStyleSheet($doc); $doc->load($xml); $output = html_entity_decode($xsl->transformToXML($doc)); $this->addCache($output , $this->source . '.xml'); echo $output; } } /** * the following cache mechanism * was taken from * http://www.phpro.org/classes/Template-Class.html */ private function addCache($content, $file){ /** * cache filename */ $filename = $this->cache_dir . '/' . basename($file); /** * directory name for the cache file */ $directory = $this->cache_dir; /** * create the cache directory */ @mkdir($directory, 0775); /** * write to the cache */ if(file_put_contents($filename, $content) == FALSE){ throw new Exception("Unable to write to cache"); } } /** * gets the content of the cache file */ private function getCache($file){ $filename = $this->cache_dir . '/' . basename($file); /** * read the cache file into a variable */ $content = file_get_contents($filename); return isset($content) ? $content : false; } /** * checks if a file is cached, based on the time * specified in cache_lifetime */ public function isCached($file){ $filename = $this->cache_dir . '/' . basename($file); if (is_file($filename)){ clearstatcache(); if (filemtime($filename) > (time() - $this->cache_lifetime)){ $isCached = true; } } return isset($isCached) ? true : false; } }
The XSL
And finally, we will define an XSL Stylesheet that will be used to transform the XML into HTML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="iso-8859-1" indent="no"/> <xsl:template match="channel"> <xsl:apply-templates select="item[1]" /> </xsl:template> <xsl:template match="item"> <h3> <xsl:value-of select="title"/> </h3> <div class="line"></div> <p> <xsl:value-of select="description"/> </p> <a class="button" target="_blank"> <xsl:attribute name="href"> <xsl:value-of select="link"/> </xsl:attribute> Read more </a> </xsl:template> </xsl:stylesheet>
The concept is pretty simple. We are expecting the following structure in the RSS XML:
<channel> ... <item> <title> </title> <link> </link> ... </item> </channel>
First we select the node called channel and select the first item from it. Then we apply the template to the item node. That will give us the structure with the title, the description and the link to the original article.
The JavaScript
When clicking on a blog link in the list, we will get the information needed through an AJAX request to the PHP class and put it into our modal box. We will define the functions showModal() and hideModal() that will take care of sliding the modal box in and out (i.e. showing/hiding it).
We will animate the modal box from the outer left (negative value) to the center of the page. Note that we define in the CSS that the margin-left is -250px. If we move the modal box to left:50%, we make it appear horizontally centered in the page with that negative margin. To hide the box then, we move it to the right by saying that the left is 150%.
The functions for getting the next and previous blog’s latest post makes use of the current variable that tracks at which latest post we are right now.
$(function() { /** * solve IE AJAX cache problem */ $.ajaxSetup({cache: false}); /** * the index of the current element / blog selected * and the total number of elements / blogs */ var current = -1; var total = $('#friendsList').children().length; /** * when we click on a blog link, * we get the latest post with an AJAX request */ $('#friendsList a').bind('click',function(e){ var $this = $(this); showModal(); var $elem = $this.parent(); current = $elem.index() + 1; var source = $elem.attr('class'); /** * add the title and image of the blog */ $('#blog_info').empty() .html('' + $this.find('em').html()); $.get('rss.class.php', {source:source} , function(data) { $('#latest_post').removeClass('loading').html(data); }); e.preventDefault(); }); /** * show the modal box with the post */ function showModal(){ $('#overlay').show(); if(!$('#modal').is(':visible')) $('#modal').css('left','-260px') .show() .stop() .animate({'left':'50%'}, 500); } /** * hide the modal box */ function hideModal(){ $('#modal').stop() .animate({'left':'150%'}, 500, function(){ $(this).hide(); $('#overlay').hide(); $('#latest_post').empty(); }); } /** * clicking on the cross hides the modal box */ $('#modal .close').bind('click',function(){ hideModal(); }); /** * clicking on the next on the modal box */ $('#modal .next').bind('click',function(e){ if(current == total){ e.preventDefault(); return; } $('#latest_post').empty().addClass('loading'); $('#friendsList li:nth-child('+ parseInt(current+1) +')').find('a').trigger('click'); e.preventDefault(); }); /** * clicking on the prev in the modal box */ $('#modal .prev').bind('click',function(e){ if(current == 1){ e.preventDefault(); return; } $('#latest_post').empty().addClass('loading'); $('#friendsList li:nth-child('+ parseInt(current-1) +')').find('a').trigger('click'); e.preventDefault(); }); });
And that’s it! I hope you enjoyed the tutorial and found it useful!
Amazing Work Guys!!! WOW : )
This is a very unique use of jquery that I haven’t seen before. I’ll be looking to implement this on my own site. Thanks for posting. 🙂
That’s pretty sweet, well done! Looks very professional and your code is very clear.
Keep up the great work!
Good very good…. I need to write in my langage….
” Bravo c’est un tuto clair et très bien expliqué. on a une bonne vision des différentes technologies mis en œuvre. “
Thank you all so much! I am glad you like it! Cheers, ML
Nice tut! And it is great that you put in Wordrom there! Keep up the good work.
beautiful work. so elegant and useful. thanks for adding my site in the demo!
Awesome! A great example in both PHP and jQuery. Especially like the elegance of XSLT.
And thank you for including Tzine 🙂
@Vincent @Michael @Martin Thank you all for your great feedback! It is a pleasure and honor to include your great blogs! Cheers, ML
Very nice tut. This could be a part of my latest project, if I am allowed to “redesign” it (it is a commercial site).
Thank you for designing this useful “thingies” 🙂
@Michael Thank you for asking, of course you can use it and redesign it as you wish! Cheers, ML
Thanks u r information
its very useful
Very useful tutorial and came just in time for me. I’ve been wanting to make something like this for a while now. Thanks a lot!!
why i have this error:
Warning: DOMDocument::load() [domdocument.load]: I/O warning : failed to load external entity “http://feeds.feedburner.com/ourtutsfeed?format=xml” in D:\xampp\htdocs\LatestPostSlider\rss.class.php on line 86
Read more
Yes. this is very timely and a great resource.
Excellent work! How can someone modify it, so that it loads an html table instead of http xml request?
is this SEO friendly ? can it be ?
Gr8 post mate..
Thanks for sharing such amazing modules , with descriptions and open code !
Wow, really amazing work on jQuery and PHP. I have never seen this unique use of jQuery. Excellent and beautiful work guys. Thanks for sharing it.
I really love how this looks and works! Thank you!
Excellent, really cool and usefull!!! Thanks for teach…
Thanks Very Nice