HTML, CSS and JavaScript

General client-side web development stuff

Clone-Noding to IFrame and accurate positioning in cross-browser animation

When doing cross-browser DHTML/CSS2 based animation, interactive or not, a major issue that emerges is accurate relative placement of absolute positioned elements in different browsers. The more complex the host page structure becomes the more the danger of relative placement of elements being ok in one browser and off in another.

A good solution to this problem is doing all DHTML animation within specially reserved iframes. The reason this is a solution is that all placement within the iframe is done using coordinates relative to the iframe's body root and not the hosting document's body root. However, using iframes in this way may require using DHTML to actually populate the iframes with elements and/or perform other manips on their DOMs.

Here is two alternative methods to clone all elements within a container element under the hosting document to an iframe's document body. You just need to pass the host body's container element and the iframe's body or the iframe itself in the second one. Note that document.getElementById() can only fetch elements of the host document. You would need to run iframe.contentWindow.document.getElementByid() to get a ref to an element in the iframe's doc.

 function moveElementContentsToIFrameElement(el, ifelBody){
     var e, els;
     if (el.hasChildNodes()) {
         els = el.childNodes;
         var o;
         for (var i = 0; i < els.length; i++){
             e = els[i];
             //copy each child as child of target iframe node
             o = e.cloneNode(true);
             //alert(ifelBody);
             ifelBody.appendChild(o);
         }                                       
         //finally remove elements container
         el.parentNode.removeChild(el);
    }
}

function moveElementContentsToIFrameElement2(el, ifel){
    ifel.contentWindow.document.writeln(el.innerHTML);
    el.parentNode.removeChild(el);
}		

Some other notes:

  • Most reliable way to access the iframe's document (and everything else therein) is to get a ref to the iframe tag object by id and then do iframe.contentWindow.document
  • Under IE there are separate event objects for the host and iframe docs. To get a ref to the iframe's event object do iframe.contentWindow.event

A full working example using the methods and techniques outlined in this article is here.

Creating simple HTML based reports using JavaScript and DOM

This page basically demonstrates how to programmatically manipulate the contents and structure of an HTML element (in this case a DIV) in order to produce a structured HTML based report. See source code at the bottom.

SCNI Monthly Report

Month:  Month  Year:  Year
Name:  Name
Date:  Date




-- move me --

My Details

Name:
Month:  Year:
Date:
 

My Contributions

Title:
Type:
Achievement
Relevance to EU policy or R&D
    <script language="JavaScript" type="text/javascript">
        var x,y;
        var ledger, control;
		var contributionCounter = 0;
        
        function mouseMoveHandler(e){            
            var ev;
            if (e) ev = e;
            else ev = event;
            
            //alert(ledger.style.left);
            if (ev.clientX){
                dX = parseInt(ev.clientX) - x;
                dY = parseInt(ev.clientY) - y;
            }
            else if (ev.X)
            {
                dX = parseInt(ev.X) - x;
                dY = parseInt(ev.Y) - y;
            }
            
            //window.status = dX + " - " + dY;            
            control.style.left = (parseInt(control.style.left) + dX) + "px";
            control.style.top = (parseInt(control.style.top) + dY) + "px"; 
            
            if (ev.clientX){
                x = parseInt(ev.clientX);
                y = parseInt(ev.clientY);
            }
            else if (ev.X){
                x = parseInt(ev.X);
                y = parseInt(ev.Y);            
            }
        }
        
        function ledgerDown(e){            
            var ev;
            if (e) ev = e;
            else ev = event;      
            //alert("ledgerDown()");
            if (ev.clientX){
                x = parseInt(ev.clientX);
                y = parseInt(ev.clientY);
            }
            else if (ev.X){
                x = parseInt(ev.X);
                y = parseInt(ev.Y);            
            }
            
            document.onmousemove = mouseMoveHandler;
        }
        
        function ledgerUp(e){
            var ev;
            if (e) ev = e;
            else ev = event;
               
            document.onmousemove = null;
        }
        
        function initElements(){
            document.body.onmouseup = ledgerUp;
            ledger = document.getElementById("ledger");
            ledger.onmousedown = ledgerDown;
            control = document.getElementById("control");
			var d = new Date();
            var year;
            if (d.getFullYear) year = d.getFullYear();
            else year = d.getYear();
            
	    document.getElementById("form_date").value = d.getDate() + 
                "/" + d.getMonth() + "/" + year;
	    document.getElementById("form_year").value = year.toString();
            //alert(year);
        }
		
		function addDetails(){
			var name = document.getElementById("form_name").value;
			var month = document.getElementById("form_month").value;
			var year = document.getElementById("form_year").value;
			var date = document.getElementById("form_date").value;
			document.getElementById("name").innerHTML = name;
			document.getElementById("month").innerHTML = month;
			document.getElementById("date").innerHTML = date;
			document.getElementById("year").innerHTML = year;
		}
		
		function addContribution(){
		    var title = document.getElementById("form_title").value;
                    var i = document.getElementById("form_type").selectedIndex;
		    var type = document.getElementById("form_type").options[i].value;
		    var achievement = document.getElementById("form_achievement").value;
		    var relevance = document.getElementById("form_relevance").value;
			
		    contributionCounter++;
			
		    var report = document.getElementById("report");
			
		    var el = document.createElement("DIV");
     var myHTML = "<table width=\"100%\" border=\"1\"" + 
     "style=\"border:solid black 1px\">" +
     "<tr><td colspan=\"2\"><h3>Contribution" + 
     contributionCounter + "</h3></td></tr>" +
     "<tr><td><strong>Title </strong></td>" + 
     "<td width=\"100%\">" + title + "</td></tr>" +
     "<tr><td><strong>Type </strong></td><td>" + 
     type + "</td></tr>" +
"<tr><td><strong>Achievement </strong></td><td>" +  
     achievement + "</td></tr>" +
     "<tr><td><strong>Relevance to EU policy or" +
     "R&D </strong></td><td>" + relevance + 
     "</td></tr>" +
     "</table><br>";						 						
		    el.innerHTML = myHTML;
            
                    var anchorElement = document.getElementById("anchorElement");
		    report.insertBefore(el, anchorElement);
			
		    document.getElementById("form_title").value = "";
		    document.getElementById("form_achievement").value = "";
		    document.getElementById("form_relevance").value = "";		
			
		    //document.getElementById("form_relevance").value = el.outerHTML;
		}
		
		function getReport(){
		
			alert("IMPORTANT: the report should be saved as HTML only" +
                              "(NOT \"html complete\" and NOT as .mht)");
			
			var report = window.open("about:blank","_blank");
			
			report.document.open();
			report.document.write("<html><head><title>SCNI" + 
                            "Monthly Report</title></head><body>" +
			    document.getElementById("report").innerHTML +
		            "</body></html>");
			report.document.close();
		}
		
    </script>

HTML page layout without tables

Tables are pretty effective as a page layout tool but they may be undesirable under certain circumstances. In my case, blog entries that used table based layouts (e.g. a two column article with a pic on the left and text on the right) would misrender in the blog entries listing page as part of their html tags would be cut off by the blog's backend. I needed something that would render correctly even if the closing tags were missing. Enter CSS based layout.

There are various techniques to do CSS page layout but the one I've found works the best is the "float" style based one. Here is code that makes for a nice two column layout.

<style>
	#colLeft {
		float:left;
		width:320px;
                padding:10px;
                margin:5px;
                border:solid green 1px;
        }

	#colRight {
                padding:10px;
                margin:5px;
                border:solid green 1px;
        }

        #colContainer {
                height:540px;
                padding:10px;
                margin:5px;
                border:solid red 1px;
        }
</style>

<div id="colContainer">

<span id="colLeft">
     <img align="bottom" src="/files/gal_oly-hoops.jpg">
</span>
<span id="colRight">
     some text here
</span>

</div>

It is pretty simple really: two spans within a container div with the span on the left having a style of "float:left". Also note that the left span's width style as well as the container's height are set to specfic numeric px values. This is done here in order to tailor the layout around the image's specific size; however, for two text only columns we could have used a percentages as needed. The rest of the used styles are not crucial and may be changed to whatever you prefer.

Here is what the code renders to when placed inside a blue-border parent div:





some text here

Self-sizing unlimited depth tab cascades using just plain html

outer tab 1    outer tab 2    outer tab 3    outer tab 4   
some text here

inner tab 1    inner tab 2    inner tab 3   
some text here

inner-inner tab 1    inner-inner tab 2    inner-inner tab 3    inner-inner tab 4    inner-inner tab 5   
some text here



To achieve the effect above you only need to use standard HTML (nested tables) and very basic CSS. No DHTML, colspan hacks or other gimmicks are required, making this solution very stable and browser agnostic. Obviously, this only demonstrates the static HTML part. Clicking functionality needs to be implemented by converting the tab text to links that point to whatever back end funtionality is implied and return results plus the appropriately modified tabs.

You can easily generate this kind of tabs on the back-end using recursion to repeat the nested code as marked below.

Code follows:

<style>
td.tab{
  padding-left:2px;padding-right:2px;border:solid blue 1px;xborder-bottom:1px;
}

td.tab-activated{
  padding-left:2px;padding-right:2px;border:solid blue 1px;border-bottom:0px;
}

td.tab-spacer{
  border-bottom:solid blue 1px;
}

td.content{
  padding:20px;border:solid blue 1px; border-top:0px;
}
</style>


<table width="100%">
  <tr>
    <td align="middle">
    
      <table id="mainTable" cellspacing="0" cellpadding="0">
        <tr>
          <td align="left" id="tabs_container">
            <table width="100%" cellspacing="0" cellpadding="0">
              <tr>
                <td nowrap class="tab">outer tab 1</td>
                <td nowrap class="tab-spacer">  </td>
                <td nowrap class="tab-activated">outer tab 2</td>
                <td nowrap class="tab-spacer">  </td> 
                <td nowrap class="tab">outer tab 3</td>
                <td nowrap class="tab-spacer">  </td>
                <td nowrap class="tab">outer tab 4</td>
                <td width="100%" nowrap class="tab-spacer">  </td>
              </tr>
            </table>
          </td>
        </tr>
        
        <tr>
          <td align="left" width="100%" id="content_container" class="content">
              some text here<br><br>
              
              <!-- nesting start -->              
              <table id="mainTable" cellspacing="0" cellpadding="0">
                <tr>
                  <td align="left" id="tabs_container">
                    <table width="100%" cellspacing="0" cellpadding="0">
                      <tr>
                        <td nowrap class="tab">inner tab 1</td>
                        <td nowrap class="tab-spacer">  </td>
                        <td nowrap class="tab">inner tab 2</td>
                        <td nowrap class="tab-spacer">  </td> 
                        <td nowrap class="tab-activated">inner tab 3</td>
                        <td width="100%"nowrap class="tab-spacer">  </td>
                      </tr>
                    </table>
                  </td>
                </tr>
                
                <tr>
                  <td align="left" width="100%" id="content_container" class="content">
                    some text here<br><br>
                    
                    <!-- nesting start -->
                    <table id="mainTable" cellspacing="0" cellpadding="0">
                      <tr>
                        <td align="left" id="tabs_container">
                          <table width="100%" cellspacing="0" cellpadding="0">
                            <tr>
                              <td nowrap class="tab-activated">inner-inner tab 1</td>
                              <td nowrap class="tab-spacer">  </td>
                              <td nowrap class="tab">inner-inner tab 2</td>
                              <td nowrap class="tab-spacer">  </td> 
                              <td nowrap class="tab">inner-inner tab 3</td>
                              <td nowrap class="tab-spacer">  </td>
                              <td nowrap class="tab">inner-inner tab 4</td>
                              <td nowrap class="tab-spacer">  </td>
                              <td nowrap class="tab">inner-inner tab 5</td>
                              <td width="100%" nowrap class="tab-spacer">  </td>
                            </tr>
                          </table>
                        </td>
                      </tr>
                      
                      <tr>
                        <td align="left" width="100%" id="content_container" class="content">
                        some text here<br>
                        </td>
                      </tr>
                    </table>
                    <!-- nesting end -->
                    
                  </td>
                </tr>
              </table>
              <!-- nesting end -->
          
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>