Wednesday, April 23, 2014

Reading XML from jQuery, creating a dynamic page with anchor tags and navigating to them



Yah… the title is a mouthful

I needed to import XML news items to a html page, simple as it sounds, this needed to be on the client side, (I mean all the reading and html construction) needed to happen on the browser.
Reading XML files using JavaScript is possible see…
And there is displaying XML data in html
But jQuery allows the use of Ajax, That is, jQuery allows you to asynchronously read an XML file, iterate though all the elements and use jQuery constructs to write html

My XML file… news.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<NewsList>
       <NewsItem>
              <Id>1</Id>
              <Name>News item 1</Name>
              <thumbimage>images/thumbs/thumb1.jpg</thumbimage>
              <image>images/img1.jpg</image>
              <Description>DESC1</Description>
       </NewsItem>
       <NewsItem>
              <Id>2</Id>
              <Name>News item 2</Name>
              <thumbimage>images/thumbs/thumb2.jpg</thumbimage>
              <image>images/img2.jpg</image>
              <Description>DESC2</Description>
       </NewsItem>
       <NewsItem>
              <Id>3</Id>
              <Name>News item 3</Name>
              <Description>DESC2</Description>
       </NewsItem>
</NewsList>
The Html page consists of a single element (div tag) to which our data shall be appended using jQuery.


<div id= "NewsDiv"></div> 

On the ready function jQuery Ajax is used to read the xml and iterate through the news items…

<script>
        $(document).ready(function () {
            $.ajax({
                type: "GET",
                url: "news.xml",
                dataType: "xml",
                success: function (xml) {
                    $(xml).find('NewsItem').each(function () {
                        var id = $(this).find('Id').text();
                        var name = $(this).find('Name').text();
                        var image = $(this).find('image').text();
                        var desc = $(this).find('Description').text();
                        //the styles are to make them look ok… I guess…
                        $('<div id="link_' + id + '" style="width:400px"></div>').html('<div style="border:2px solid red;text-align:center">' + name + '</div><div style="text-align:left;padding:10px 3px 3px 10px"><p><img src="' + image + '" style="float:left;padding:8px" />' + desc + '</p></div>').appendTo('#NewsDiv');
                    });
                }
            });
        });
</script>

Above code reads the items, creates html, and appends it to the NewsDiv.
Well, If you notice in the XML, the item 3 does not have an image, hence if condition

if(image!='')
                           $('<div id="link_'+id+'" style="width:400px"></div>').html('<div style="border:2px solid red;text-align:center">'+name+'</div><div style="text-align:left;padding:10px 3px 3px 10px;border:2px solid black"><p><img src="'+image+'" />'+desc+'</p></div>').appendTo('#NewsDiv');
                           else
                           $('<div id="link_'+id+'" style="width:400px"></div>').html('<div style="border:2px solid red;text-align:center">'+name+'</div><div style="text-align:left;padding:10px 3px 3px 10px;border:2px solid black"><p>'+desc+'</p></div>').appendTo('#NewsDiv');



Until now, it was the basics to read XML and display on a page.
In a basic site, you would have a news ticker on the main page where each item links to the main news page.
The idea is to create a new page, let’s say the index page where the new items are shown in short (the thumbnail in the XML file is used for this) each item links to the news page.
Simply, we have this


Adding a little more functionality would enhance this more
What if…

 

Well that’s easy use named anchor tags
Named anchor tags are links on the same page, you must have seen the ‘top’ like on many sites that navigates to the top of a page. Similarly we can link each news item to an anchor tag and navigate to that part of the page when the news item is clicked.

The basic code to be used is…
<a name=position>POSTION</a>
   - position on a page
<a href=#position>navigate to postion</a>
- navigate to the position
To navigate to a part of the page from another page we have to mention the anchor name in the url with the hash like…
<a name=position>POSTION</a>
- position on a.html
<a href=a.html#position>navigate to postion</a>
- navigate to the position from b.html
Or you could as Mr Google for more details
This calls for some changes in the code in the main page (that shows all the detailed news items) news.html
The jQuery code to write the html need changes by adding a named anchor for each item.
Just need to insert this… <a name="+id+"></a>
since we already have the id of the news item we need to use it to add the anchor
Therefore the script has a minor change


    if(image!='')
                           $('<div id="link_'+id+'" style="width:400px"></div>').html('<div style="border:2px solid red;text-align:center">'+name+' <a name="'+id+'"></a></div><div style="text-align:left;padding:10px 3px 3px 10px;border:2px solid black"><p><img src="'+image+'" />'+desc+'</p></div>').appendTo('#NewsDiv');
                           else
                           $('<div id="link_'+id+'" style="width:400px"></div>').html('<div style="border:2px solid red;text-align:center">'+name+' <a name="'+id+'"></a></div><div style="text-align:left;padding:10px 3px 3px 10px;border:2px solid black"><p>'+desc+'</p></div>').appendTo('#NewsDiv');

Now that we have the anchor set for each item on the news page lets make news items on the main page.
Create a new page called index.html that will contain the ticker
Keeping it simple…. Just add one div tag called newsticker

<div id="newsticker"  style="height:210px;overflow-x:hidden;overflow-y:auto"></div></div>

This will contain only the name and the thumbnail (if present)
In the script of the index.html

<script>
        $(document).ready(function () {
            $.ajax({
                type: "GET",
                url: "news.xml",
                dataType: "xml",
                success: function (xml) {
                    var newstable = '<table border="1" align="center" cellpadding="20px" width="90%"><tbody>';

                    $(xml).find('NewsItem').each(function () {
                        var id = $(this).find('Id').text();
                        var name = $(this).find('Name').text();
                        var desc = $(this).find('Description').text();
                        var image = $(this).find('thumbimage').text();
                        if (image != '') {
                            newstable += '<tr><td><a href="news.html#' + id + '"><img src="' + image + '" style="float:left;padding:8px" />' + name + '</a></td>'
                        }
                        else {
                            newstable += '<tr><td><a href="news.html#' + id + '">' + name + '</a></td>'
                        }
                    });
                    newstable += '</tbody></table>';
                    $('#newsticker').html(newstable);

                }

            });
        });
</script>

This shows a table that has the names and the thumbnail images
So, here we are, we created two pages that read from a XML file and display the contents, and link the news ticker items to their corresponding detailed items.
The problem
This would work perfectly well if the page was not created dynamically
The problem is, the named anchor link from the main page cannot read the link on the news page as it’s not created as soon as the page is loaded.
Therefore, we need to wait until the page is loaded and then navigate to the anchor tags
For this we need to use javascript.
To navigate to a named anchor on a page using javascript,

 


location.hash = '#'+namedanchor;



we need to first get the name of the anchor from the url, for this…

var pathname = window.location.pathname;

or in jQuery


 
var pathname = $(location).attr('href');

After this, the pathname is split to get the name of the anchor…

var hash = '#'+pathname.split('#')[1];
then we use the setTimeout function to set a delay…

setTimeout(function(){location.hash = hash;}, 100);

Complete code:

              var pathname = $(location).attr('href');
              var hash = '#'+pathname.split('#')[1];
              setTimeout(function(){
                     location.hash = hash;
              }, 100);

and so when this piece of code is added to the news.HTML script, the page loads up fine and points to the anchor.
To run this javascript after the page is loaded this code can be used

$(window).bind("load", function() {
   // code here
});
But the delay works just fine (in stand alone conditions).
Its better if we use this

$(window).bind("load", function() {
   var pathname = $(location).attr('href');
              var hash = '#'+pathname.split('#')[1];
              setTimeout(function(){
                     location.hash = hash;
              }, 400);
});
Hope this helps someone, and if you know a better way to delay the script, please let’s have a conversation in the comments below.