Official website for Web Designer - defining the internet through beautiful design
FOLLOW US ON:
Author: Steve Jenkins
5th February 2013

Create a responsive news content widget

Display the latest news headlines in an image slider style content switcher that responds to screen size

Create a responsive news content widget

There are hundreds of scripts and plug-ins online for content sliders using JavaScript. Most of those scripts are for image-heavy content that is geared to making a visual impression. This one is a little different. The idea for this tutorial comes from a content switcher that, at the time of writing, appears on the homepage for globalnews.ca (a Canadian news network). In this tutorial, we will be recreating that widget.

This content switcher is text-heavy and is focused on displaying the latest top headlines for that day. It has a vertical list of headlines, along with a lava lamp-like effect where a white bar highlights the currently visible news story. In addition to allowing the user to click to view any headline, the highlight bar animates automatically, cycling through all the available items.

The version on globalnews.ca uses JavaScript for the white bar animation, and the widget isn’t responsive. We’ll improve on that by making ours responsive and use CSS3 transitions for the lava lamp effect.
If you’d like to fiddle with the full code for this widget online, you can view it at this JS Bin: jsbin.com/utaneq/48/edit, or use the files with this tutorial.

DOWNLOAD TUTORIAL FILES

The headlines list

The first thing we’ll do is establish some clean, semantic markup to hold the content that our script will manipulate. We’ll start with a simple unordered list, with the last item being a special “highlight item” that we’ll use to create the lava lamp effect. We’ll also have a default “selected” list item, and we’ll throw in a couple of phony news headlines.

001 <ul>
002     <li>100 red bicycles stolen from local bike store</li>
003     <li>New leash laws in effect for floppy-eared dogs</li>
004     <li>Insider: Can palm trees be saved?</li>
005     <li>Fresh recipes to titillate the taste buds</li>
006     <li>Truck inspections under way for the metropolitan area</li>
007     <li>Are the beaches safe for swimming this year?</li>
008     <li></li>
009 </ul>

The news preview

The right-side of our widget will hold the individual news items. They’ll each have an image, some text, and some css classes for styling and scripting purposes. Our example will consist of six news items. Here’s how each item will be marked up:

001 <div>
002        <img src=”images/news1.jpg” width=”220″ height=”143″ alt=”100 red bicycles stolen from local bike store”>
003        <p><a href=”#”>100 red bicycles stolen from local bike store</a></p>
004          
005        <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
006 </div>

Fixed box model

To ensure width calculations are more intuitive, we’ll set the box-sizing property, along with some universal margin and padding resets (which will usually be done in a CSS reset, so you likely won’t need this):

001 * {
002    margin: 0;
003    padding: 0;
004    -webkit-box-sizing: border-box;
005    -moz-box-sizing: border-box;
006    box-sizing: border-box;
007 }


Adding a transition

To avoid using jQuery for the highlight bar animation, we’ll add some CSS3 transitions to the headline list items. We also add some innocuous transforms to help the animation look smoother. The z-index value is important to help the white highlight bar (which has a lower z-index) appear below all the list items in the stack.

001 .news-headlines li {
002    padding: 5px 20px 5px 24px;
003    margin-bottom: 15px;
004    position: relative;
005    z-index: 20;
006    -webkit-transition: all .75s 
ease-out;
007    -moz-transition: all .75s ease-
out;
008    -o-transition: all .75s ease-out;
009    transition: all .75s ease-out;
010    color: #336699;
011    -webkit-transform: translateZ(0);
012    -moz-transform: translateZ(0);
013    -o-transform: translateZ(0);
014    transform: translateZ(0);
015 }

Pseudo-element bullets

To create the list item bullets, we’ll use pseudo-elements. This helps us avoid adding an unnecessary image. The pseudo-element is a small red box set to display: inline-block and margin settings help position it correctly. We also ensure that the cursor changes to a hand when the list item is hovered (since we’re not using <a> elements).

001 .news-headlines li:before {
002    content: “”;
003    display: inline-block;
004    width: 5px;
005    height: 5px;
006    background: red;
007    vertical-align: middle;
008    margin-left: -12px;
009    margin-right: 7px;
010 }
011 
012 .news-headlines li:hover 
013 {
014    cursor: pointer;
015 }

Stacked news items

On the right-side of the widget we’ll display content associated with the selected news item. Each of these items will be absolutely positioned to help stack them so only one is visible at a time. A separate class will be used to bring the selected news item to the top of the stack with z-index:

001 .news-content {
002    position: absolute;
003    background: white;
004    z-index: 10;
005    padding: 10px;
006    top: 0;
007    left: 0;
008 }
009 
010 .top-content {
011    z-index: 50; 
012 }

Negative margin

We want the white highlight bar to appear as though it’s flowing right into the content area on the right-side of the widget. To do this, we’ll apply a negative margin to the content section, along with a z-index value that’s lower than that of the highlight bar itself.

001 .news-preview {
002   float: left;
003   border: solid 1px #999;
004   width: 51%;
005   background: white;
006   position: relative;
007   z-index: 5;
008   margin-left: -1px;
009   min-height: 304px;
010   position: relative;
011 }

Responsive images

This isn’t a fully-fledged responsive images solution that uses different image resolutions, but instead we’ll just add some simple CSS to help keep the image at a maximum size while reducing its size for smaller devices. The key parts of this code block are the width, height, max-width, and max-height properties:

001 .news-preview img 
002 {
003    display: block;
004    border: solid 1px #999;
005    width: 100%;
006    height: auto;
007    max-width: 220px;
008    max-height: 143px;
009    margin: 0 auto 5px auto;    
010 }

The highlight bar

Finally, the last list item in our headlines list is an empty element that’s used as the highlight bar, to indicate the current selected item. Semantic purists might want to inject this element with JavaScript to keep the HTML clean. We’ll apply the following default styles to help position it:
001 .news-headlines .highlight {
002     width: 100%;
003     background: white;
004     border-top: solid 1px #999;
005     border-left: solid 1px #999;
006     border-bottom: solid 1px #999;
007     position: absolute;
008     top: 0;
009     left: 0;
010     z-index: 10;
011 }

Defining our variables

The first thing our script will do is cache some objects that we’ll refer to multiple times in our code. Then we’ll initiate some utility variables, some of which are set initially to null because they depend on values that aren’t constant. The vPadding and vMargin variables are used to calculate the size of the headline items so that it can be fully controlled with CSS and thus will be more maintainable.

001 var hl = $(‘.highlight’),
002    newsList = $(‘.news-headlines’),
003    newsListItems = $(‘.news-headlines li’),
004    firstNewsItem = $(‘.news-headlines li:nth-child(1)’),
005    newsPreview = $(‘.news-preview’),
006    elCount = $(‘.news-headlines’).children(‘:not(.highlight)’).index(),
007    vPadding = (parseInt(firstNewsItem.css(‘padding-top’).replace(‘px’, ”), 10)) + (parseInt(firstNewsItem.css(‘padding-bottom’).
replace(‘px’, ”), 10)),
008    vMargin = (parseInt(firstNewsItem.css(‘margin-top’).replace(‘px’, ”), 10)) + (parseInt(firstNewsItem.css(‘margin-bottom’).
replace(‘px’, ”), 10)),
009    myTimer = null,
010    siblings = null,
011    totalHeight = null,
012    indexEl = 1,
013    i = null;

Equal height columns

Our widget is divided into two columns. To ensure the sides are relatively equal, even after the user resizes the browser window, we’ll create a function that will equal out the columns depending on which side is bigger. The function uses a min-height value set in the CSS, again keeping this data outside the script itself.

001 function doEqualHeight() {
002    
003   if (newsPreview.height() < newsList.height()) {
004      newsPreview.height(newsList.height());
005    } else if ((newsList.height() < newsPreview.height()) && (newsList.height() 
> parseInt(newsPreview.css(‘min-height’).replace(‘px’, ”), 10))) {
006      newsPreview.height(newsList.height());
007    }
008    
009 }

Auto change

We want the widget to cycle through the headlines automatically. For this, we’ll create a function that uses JavaScript’s setInterval() method. This function will trigger a click event every ten seconds. Each time the click event is triggered, the selected news item changes. If the current selected element is the last element, it will cycle back to item one.

001 function doTimedSwitch() {
002  
003    myTimer = setInterval(function () {
004      if (($(‘.selected’).prev().index() + 1) === elCount) {
005        firstNewsItem.trigger(‘click’);
006      } else {
007        $(‘.selected’).next(‘:not(.
highlight)’).trigger(‘click’);
008      }
009    }, 10000);
010  
011 }

The click function

Next, let’s begin our primary function, the doClickItem() function. We’ll start by adding the click event to our news items. This event gets triggered either manually by the user, or automatically by the script, using jQuery’s trigger() method. We remove any instances of the selected class, and add the selected class to the currently clicked item.

001 function doClickItem() {
002
003  newsListItems.on(‘click’, function () {
004
005    newsListItems.removeClass(‘selected’);
006    $(this).addClass(‘selected’);
007 
008    // further code is added here in 
subsequent steps…
009 
010  });
011
012 }

Calculate highlight position

Lets add to our doClickItem() function. We want to find out how many items appear before the current highlighted item. Once we know that (which we get using jQuery’s prevAll()), we set the totalHeight variable to zero, and then loop through the items. We add up the total height of all the items, plus any vertical padding and margins.
001 siblings = $(this).prevAll();
002 totalHeight = 0;
003 
004 for (i = 0; i < siblings.length; i += 1) {
005     totalHeight += $(siblings[i]).height();
006     totalHeight += vPadding;
007     totalHeight += vMargin;
008 }

Move the highlight

Using the totalHeight calculation from the previous step, we use jQuery’s css() method to set the top and height values of the highlight element (cached in our variables as hl). Because we’ve set a CSS3 transition on the list items using the all keyword (which means transition all properties), the top and height values will animate when changed with JavaScript.

001 hl.css({
002     top: totalHeight,
003     height: $(this).height() + vPadding
004 });

Show news item

Further adding to our doClickItem() function, we find out the index of the current highlighted element using jQuery’s index() method, adding one to the result to account for zero-based indexing. Once we know that, we use that number to add the top-content class to the corresponding .news-content element, thus putting it at the top of the stack so it’s visible.

001 indexEl = $(this).index() + 1;
002
003 $(‘.news-content:nth-child(‘ + indexEl + ’)').siblings().removeClass(‘top-content’);
004 $(‘.news-content:nth-child(‘ + indexEl + ’)').addClass(‘top-content’);

Finishing up doClickItem()

Finally, to wrap up our doClickItem() function, we use JavaScript’s clearInterval() method to stop the timer, and then we start it again by calling the doTimedSwitch
() function we created earlier. We stop the timer to ensure ten seconds is spent on the newly clicked item.

001 clearInterval(myTimer);
002 doTimedSwitch();

Window resizing

To ensure that our widget looks decent if the window is resized by the user, we’ll set up a function that uses jQuery’s $(window).resize() method. This method will execute an anonymous function each time the window is resized. The first thing we’ll do in that function is stop the timer.

001 function doWindowResize() {
002  $(window).resize(function () {
003    clearInterval(myTimer);
004  }); 
005 }

Correct the heights

The doWindowResize() function continues when we use JavaScript’s setTimeout() method to trigger a delayed click event on the selected item (to account for time spent resizing the window). This ensures that the white highlight bar is the correct height. Finally, we ensure the two sides of the widget are equal in height by calling our doEqualHeight() function.

001 function doWindowResize() {
002  $(window).resize(function () {
003    clearInterval(myTimer);
004    setTimeout(function () { 
005 $(‘.selected’).trigger(‘click’);
006    }, 1000 );
007    doEqualHeight();
008  }); 
009 }

Initiate the script

Finally, now that we have defined all of our variables and functions, we execute all three functions and then trigger a click event on the currently selected element to get things started.

001 doClickItem();
002 doWindowResize();
003 setTimeout(function () {
004    doEqualHeight();
005 }, 500);
006 $(‘.selected’).trigger(‘click’);

  • Tell a Friend
  • Follow our Twitter to find out about all the latest web development, news, reviews, previews, interviews, features and a whole more.