Building A Web App – A Scrolling News Region

Note: This post is a continuation of a series of posts on the design and development of a web application for a non-profit organization. Previous Posts:

During this post I will add a feature to the web site that allows visitors to browse news stories that are related to the web site’s content. These new stories are generated by outside sources. The info will be summarized and a link to the story provided. Here is a screenshot and a demo page.

image

Demo

HTML

The stories for the feature are contained in an un-ordered list (‘ul’ / ‘li’). This list is wrapped with a ‘div’. The following markup is used to create the demo page:

<div id="wrapper">

    <div class="demo">
        <a href="http://blog.bobcravens.com" class="demo">Bob <span>Cravens</span> Dotcom Demo Page</a>
    </div>

    <div id="news">
        <h1 class="header">In The News</h1>
        <div class="slider">
            <ul>
               <li>
                    <a href="http://en.wikipedia.org/wiki/Beach" target="_blank">
                        <img src="images/beach.jpg" alt="beach" />
                        <span class="title">Cum dolorem delectus tractatos no, at admodum.</span>
                        <span class="summary">In est inani mnesarchum, at quis epicurei duo, vel eu saepe eripuit mnesarchum. Reque partem civibus his ex, falli percipitur sadipscing id has. Vel eu falli dicant scripta, fugit graecis salutatus id pro, paulo ponderum mel eu.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Bird" target="_blank">
                        <img src="images/bird.jpg" alt="bird" />
                        <span class="title">Id probo probatus nam, veri numquam nonummy.</span>
                        <span class="summary">Quo no malis perfecto, mutat fabellas pertinax ne cum. Omittam urbanitas et sea, has ei idque dicant. Est eu ubique iuvaret accusamus.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Lighthouse" target="_blank">
                        <img src="images/light_house.jpg" alt="light house" />
                        <span class="title">Perfecto gloriatur est in, te sit eros.</span>
                        <span class="summary">Et vidit hendrerit mea, cu sumo dolore labores ius, an epicurei prodesset philosophia cum. Viris zzril numquam usu ne, his nemore sapientem no.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Mountain" target="_blank">
                        <img src="images/mountain_lake.jpg" alt="mountain lake" />
                        <span class="title">Ea latine lobortis persequeris sea, duo altera.</span>
                        <span class="summary">Sit no oratio reprehendunt comprehensam, quod legimus nominavi ad quo. Vix te odio aliquyam, malis facer est ea. Ex feugait adolescens conclusionemque his, eos nonumy noster definitionem id.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Rainbow" target="_blank">
                        <img src="images/rainbow.jpg" alt="rainbow" />
                        <span class="title">Vix ea everti animal laoreet, pro malis.</span>
                        <span class="summary">Vidit ponderum nam eu. Vim cu summo adversarium, sonet congue in quo, has at aperiri euismod contentiones. Mundi bonorum convenire te duo.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Red_sky#True_lore.2C_and_why" target="_blank">
                        <img src="images/red_sky.jpg" alt="red sky" />
                        <span class="title">Illum atqui id pro, mea tale congue.</span>
                        <span class="summary">Ex eam porro sonet, mel at ridens accommodare, has novum impedit nonummy et. Minimum fierent suscipit pro et, ut est diceret maiestatis. Vix ullum doctus bonorum eu, brute philosophia ad duo. An fabulas molestiae duo.</span>
                    </a>
                </li>
                <li>
                    <a href="http://en.wikipedia.org/wiki/Blue_Jay" target="_blank">
                        <img src="images/blue_jay.jpg" alt="blue jay" />
                        <span class="title">At sed ludus neglegentur. Probatus deterruisset ne.</span>
                        <span class="summary">Quod dolores hendrerit eam no, legere labore ne vix. Sed ne option elaboraret dissentiet. Per probatus sapientem cotidieque in, et adipisci pertinax his.</span>
                    </a>
                </li>
            </ul>
        </div> <!--slider-->
    </div> <!--news-->
</div> <!--wrapper-->

The bulk of this markup defines the news story content. Each story is encapsulated in one ‘li’ element. Inside the ‘li’ is an anchor tag with a link to the external source for the news story. The story content is made up of an image (‘img’ tag), a title (‘span’ tag with a ‘title’ class), and a summary (‘span’ tag with a ‘summary’ class).

The ‘div’ with class of ‘slider’ is used to setup a scrolling region using CSS (see below). The ‘div’ with the class of ‘news’ is used as a container for the scrolling area and a title (‘h1’ element). Finally, the ‘div’ with the class ‘wrapper’ is used to set the width of the feature. The reasons for these elements will become more clear after we look at some CSS.

CSS

The following is the cascading styles that create the horizontal scrolling area and style the elements of the stories.

#wrapper
{
    width: 900px;
    margin: 10px auto;
}

#news
{
    background-color: #eee;
    margin: 12px 0;
}

.header
{
    background: transparent url("Images/nav_bg.png") repeat-x scroll 0 0;
    color: #fff;
    font-size: 14px;
    padding: 8px 25px;
    text-transform: uppercase;
}

.slider
{
    overflow: auto;
    margin: 5px 10px 10px 10px;
}

.slider ul
{
    list-style: none;
    width: 2040px;
}

.slider ul li
{
    float: left;
    width: 212px;
    padding: 5px;
}

.slider ul li a,
.slider ul li a:visited
{
    background-color: #fff;
    border: solid 1px #fff;
    text-decoration: none;
    display: block;
    padding: 3px;
    height: 290px;
}

.slider ul li a:hover
{
    background-color: #eee;
    border: solid 1px #888;
}

.slider ul li span.title
{
    color: #193c74;
    font-size: 1.5em;
    font-weight: bold;
    display: block;
}

.slider ul li span.summary
{
    color: #333;
    font-size: 12px;
    margin: 5px 0 0 0;
    display: block;
}

The width on the HTML ‘div’ element with ID of ‘wrapper’ constrains the elements it contains. Normally outer elements grow to a size so they can contain their children. Scroll bars are then added to the HTML ‘body’ element. This is the normal behavior and that we need to over-ride.

Setting the overflow property to ‘auto’ on the ‘.slider’ class creates a region that automatically provides scroll bars for elements that are larger than the area can display. The width on the HTML ‘ul’ element is set large enough to allow the HTML ‘li’ elements to be displayed side by side. In this example that width is 1554 pixels. This is larger than the 900 pixels allows by the wrapper. This overflows the allotted area and the browser will automatically display a horizontal scroll bar. In addition to the width the ‘list-style’ property removes the traditional bullet points rendered with unordered lists.

Each HTML ‘li’ contains a single story. The ‘li’ elements are ‘floated left’. This allows the ‘li’ elements to be displayed side by side. The width and padding on the ‘li’ element create a total with of 222 pixels. Since there are 7 stories the total width is 1554 pixels. This is the width we set earlier on the ‘ul’ element.

The rest of the CSS is ‘gold plating’ to color backgrounds, set borders, and hover over effects. Although these are not necessary to create the sliding new story effect, they do enhance the user experience.

JavaScript

There is a small amount of JavaScript to make the experience solid. If we set the width of the ‘ul’ element too small to contain the story ‘li’ elements then we will get wrapping of the stories. If the width is too large, there will be empty space in the scrolling area. The following jQuery sets the width to the amount required by the stories.

var liList = $("div.slider ul li");
var count = liList.length;
var liWidth = 0;
if(count>0){
    var li = $(liList.get(0));
    liWidth = li.outerWidth();
}
var totalWidth = count * liWidth;
$("div.slider ul").width(totalWidth);

This JavaScript calculates the width required by multiplying the number of stories (number of ‘li’ elements) by the width required by each story. The required width is calculated using the jQuery outerWidth function that includes the margin and padding in the calculation.

ASP.NET MVC – View

This application that is being developed is leveraging the ASP.NET MVC framework on the server. The following is the partial view that is used to render the client side HTML.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NewsService>" %>
<%@ Import Namespace="Heartland.Services" %>

<div id="news">
    <h1 class="header">In The News</h1>
    <div class="slider">
        <ul>

            <% foreach (Story story in Model.Stories) { %>

                <li>
                    <a href="<%= story.Link %>" target="_blank">
                        <img src="Content/InTheNews/<%= story.ImageUrl %>" alt="<%= story.ImageAlt %>" />
                        <span class="title"><%= story.Title %></span>
                        <span class="summary"><%= story.Summary %></span>
                    </a>
                </li>

            <% } %>

        </ul>
    </div>
</div>

This code uses a ‘foreach’ loop to generate the un-ordered list HTML markup. The view is strongly typed and is passed in a view model of type ‘NewsService’ . This partial view is rendered into the main view.

ASP.NET MVC – Model

The view model contains the information required by the view to create the HTML. The following defines the view model.

public class Story
{
    public string ImageUrl { get; set; }
    public string ImageAlt { get; set; }
    public string Title { get; set; }
    public string Summary { get; set; }
    public string Link { get; set; }
    public bool IsShown { get; set; }
}

public class NewsService
{
    public List<Story> Stories { get; set; }

    public NewsService()
    {
        Stories = new List<Story>();
    }


    public static NewsService ReadConfig(string configFile)
    {
        return XmlData.DeserializeFromFile<NewsService>(configFile);
    }

    public static void SaveConfig(NewsService newsService, string configFile)
    {
        XmlData.SerializeToFile(newsService, configFile);
    }
}

The ‘NewService’ class has two static methods that are used to serialize / de-serialize data from an XML file. This file is used to persist story data and allows the news story data to be updated with out changing the HTML or code. 

ASP.NET MVC – Controller

The ‘Index’ action method for the controller is lean and shown in the following code:

public class HomeController : Controller
{
    private SlideService _slideService;
    private NewsService _newsService;

    public SlideService SlideService
    {
        get
        {
            if (_slideService == null)
            {
                string configFileSlides = Request.MapPath("~/Content/Slides/SlideConfig.xml");
                _slideService = SlideService.ReadConfig(configFileSlides);
            }
            return _slideService;
        }
    }

    public NewsService NewsService
    {
        get
        {
            if (_newsService == null)
            {
                string configFileNews = Request.MapPath("~/Content/InTheNews/NewsConfig.xml");
                _newsService = NewsService.ReadConfig(configFileNews);
            }
            return _newsService;
        }
    }

    public HomeController()
        :this(null, null)
    {

    }

    public HomeController(SlideService slideService, NewsService newsService)
    {
        _slideService = slideService;
        _newsService = newsService;
    }

    public ActionResult Index()
    {
        HomeViewModel hvm = new HomeViewModel
                                {
                                    SlideService = SlideService,
                                    NewsService = NewsService
                                };

        return View(hvm);
    }
}

The two constructors allow the controller to be tested by injecting in fakes for the model contents. The constructor takes two parameters. The ‘SlideService’ parameter contains the slide show data and the ‘NewsService’ parameter is the data for the news feature we are developing. The default constructor sets these to ‘null’ which which causes this data to be fetched from the XML data files. The ‘Index’ action method packages up the service data into a ‘HomeViewModel’ object and then injects it into the View.

Summary

In this post, we have developed a feature that renders news stories in a horizontal scroll area. An important design decision is to push the news story content into an XML file. This allows the feature to be updated by changing the XML file.

Comments
  1. App

Leave a Reply

Your email address will not be published. Required fields are marked *

*