|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionBefore starting anything, please note that this article demonstrates an advanced level of Object Oriented, Prototype-Based JavaScript Programming. If you are not familiar to Object Oriented JavaScript, you may want to read my former article or Paul Baker's mini racer which demonstrate the concept. In this article, we will try to generate a pop-up that loads an external file. What made me think on such a solution was the increase in the usage of pop-up blockers. When we implement our solution, our popup will never be blocked. Because it will not be a real pop-up at all. Actually, it will be a DHTML layer. So no popup blocker will be able to block it :) This implementation has some advantages and some disadvantages:
What we will be doing can be summarized in five steps:
As I have always been, in this article, I will try to make it as much cross-browser and object oriented as I can. I assure you, it will be fun. You may want to see the final result in action before proceeding. The LayerThis is the easy part. Although it can be made much easily with tables, I prefer to use a css-based layout. <div id="PopupMasterContainer">
<div id="PopupTopHandle">
<div id="PopupTopButtons"><img
src="images/close.gif" id="IcnClose"
alt="closes the pop-up" /></div>
<div id="PopupTopTitle">Title Goes Here</div>
</div>
<div id="PopupContent">
</div>
</div>
With some Movin' It!There are dozens of move and drag layer scripts around. So you do not have to use my version. The point is, if you are using some script, you had better know what it does rather than just copy and pasting. This way you can find a way to extend it. As I mentioned in the introduction, we will try to code OO JavaScript. We will observe examples of multi-level inheritance, method hiding, method overriding. No we won't be writing C# :) Javascript can be more object oriented than one may imagine. I've included a simplified version of my DHTML API, adding only drag-drop related parts, so that we can make our pop-up layer draggable. You can examine different usages of the full version. Or download the entire API. (pages are Turkish; the API is distributed under a non-commercial share-alike license). Not to deviate from the subject, I'll not go deep into how the drag functionality is implemented. As I said, any drag and drop API would suffice. Want to try the pop-up with drag support? When you drag the top handle, you will see that it moves. And here is the JavaScript code that does it: 1. var lyr = new DraggableLayer("PopupMasterContainer",null,null,true);
2. lyr.ignoreLayer("PopupContent");
The script turns " If you comment out line 2 you will see that not only the top handle but also the bottom content layer will activate the drag action. That's what I love about object oriented programming: encapsulation! Once you design the object properly, you rarely need to modify it. All you need to do is to know how to use the interface methods. You need not care about the code behind. I will not explain how the code does what it does. Else I will be going too out-of-scope. If you have any queries just drop me an e-mail. I'll try to respond as soon as I get time. Modeling The Pop-UpNow let us adhere to our good old model-view-controller paradigm and create a model to control our view (i.e. the pop-up). The skeleton of our model will much probably be like: function PopupLayer(elmID,strTitle) {}
PopupLayer.prototype.show=function() {}
PopupLayer.prototype.hide=function() {}
PopupLayer.prototype._closeBtn_Click=function(evt) {}
Where the Then, when we examine the API, we see that the object .moveTo(intLeft,intTop) //moves the layer to given coordinates.
.resizeTo(intWidth,intHeight)// changes width and height of the layer.
[intLeft] .getLeft()// gets the left distance from the container.
.setLeft(intLeft)//sets the left distance to the container.
[intTop] .getTop()// gets the top distance from the container.
.setTop(intTop)// sets the top distance to the container.
[intHeight] .getHeight() // gets the height of the layer.
.setHeight(intHeight) // sets the height of the layer.
[intWidth] .getWidth() // gets the width of the layer.
.setWidth(intWidth) // sets the width of the layer.
The object already defines
So, we come to a point that neither our You don't need to be an Oracle to say "Hey, PopupLayer should extend DynamicLayer !" :). Then let us do it! Extending PopupLayerAdding And here is the new prototype with some additional comments to see what we will be doing: PopupLayer.prototype = new DynamicLayer();
_this = PopupLayer.prototype;
function PopupLayer(elmID,strTitle) {
/* create a draggable - non resizable layer. */
/* set the title text */
/* attach event handler to close button */
/* override necessary methods */
}
_this._closeBtn_Click=function(evt) {}
/* override necessary methods */
And to make our object detect various parts of our container we add distinctive className's to the HTML (the additions are marked in bold). <div id="PopupMasterContainer">
<div id="PopupTopHandle" class="popupDragBar">
<div id="PopupTopButtons" class="popupButtonContainer">
<img src="images/close.gif" class="popupCloseIcon"
id="IcnClose" alt="closes the pop-up" /></div>
<div id="PopupTopTitle" class="popupWindowTitle">Title Goes Here</div>
</div>
<div id="PopupContent" class="popupConsole">
</div>
</div>
The constructor will iterate the DOM hierarchy, add event listeners, make necessary changes with the aid of those classNames. Here is the truncated version of the code in the constructor to describe what it does: function PopupLayer(elmID,strTitle) {
/* Create a draggable - non resizable layer. */
var pop = new DraggableLayer(elmID,null,null,true);
/* Find the top container. */
var objPop = pop.getObject();
var children = objPop.childNodes;
for (var i=0;i < children.length;i++ ) {
if(children[i].className) {
/* Find console. */
/* Find drag bar. */
if(children[i].className=="popupDragBar") {
for(var j=0;j < dragBarChildren.length;j++) {
if(dragBarChildren[j].className) {
/* Find and attach close event to close button. */
/* Find and set title. */
}
}//for
}//if
}//if
}//for
}//constructor
Now that we created the constructor, let us override and extend necessary parts: Overriding MethodsTo make our popup layer work the way we want, we need to override some of the inherited methods above, namely, These three methods change the Also we need to override Overriding the first three is the easy part: PopupLayer.prototype = new DynamicLayer();
_this = PopupLayer.prototype;
function PopupLayer(elmID,strTitle) {
... constructor logic ...
}
/** overridden method */
_this.changeContent = function(strNewHTML) {
...code goes here...
};
/** overridden method */
_this.addContentBefore = function(strHTML) {
...code goes here...
};
/** overridden method */
_this.addContentAfter = function(strHTML) {
...code goes here...
}
Overriding Here is what we intend to do: /** overridden show method*/
PopupLayer.prototype.show=function() {
/* Hide nasty selects. */
/* Call parent object's show method. */
}
/** overridden hide method*/
PopupLayer.prototype.hide=function() {
/* Show the selects. */
/* Call parent object's show method. */
}
Since we do not have a I think showing the code will be much explanatory than typing: PopupLayer.prototype = new DynamicLayer();
_this = PopupLayer.prototype;
function PopupLayer(elmID,strTitle) {
... constructor logic ...
this._parent_show = this.show;
this._parent_hide = this.hide;
/** Override show method. */
this.show=function() {
/* Hide dropdowns. */
/* Call parent object's show method. */
this._parent_show();
}
/** Override hide method. */
this.hide=function() {
/* Show dropdowns. */
/* Call parent object's show method. */
this._parent_hide();
}
}
Hiding / Showing DropdownsMost probably you have seen such a view somewhere else.
This rendering anomaly occurs, because HTML To prevent this, we will hide all the dropdowns in the page when the pop-up is opened, and show them again when the pop-up is closed. And gues what, we will create an object to do it :) var _this=DOMManager.prototype;
function DOMManager() {}
_this.hideCombos = function() {
var arCombo = document.getElementsByTagName("select");
for(var i=0;i < arCombo.length;i++) {
arCombo[i].style.visibility="hidden";
}
}
_this.showCombos = function() {
var arCombo = document.getElementsByTagName("select");
for(var i=0;i < arCombo.length;i++) {
arCombo[i].style.visibility="inherit";
}
}
That simple! When you need to hide combos, you call Add Another WidgetOne common request of the clients is "I want my pop-ups at the dead center of the page!". And customers are always right :) Here is an additional method to position the pop-up at the center of the page: _this.center = function() {
/*
* quick and dirty way to get
* cross-browser width and height -- not fully tested
*/
var windowHeight = self.innerHeight?
self.innerHeight:document.body.clientHeight;
var windowWidth = self.innerWidth?
self.innerWidth:document.body.clientWidth;
this.moveTo(
((windowWidth)/2-g_popup.getWidth()/2)-17
,
((windowHeight)/2-g_popup.getHeight()/2)-17
);
};
Here, we see why inheritance is a tool that we always have to keep in our toolbox:
HTTP Request to an External FileHere is what we did up till this point. We have a draggable layer which we can drag, close, show. But it is not quite the same as a real pop-up. Because, real pop-ups load HTML from an external URL. However our pseudo-popup displays merely static content. We will handle this problem in two steps:
Let us start with the easy part. With the help of _this = XHRequest.prototype;
function XHRequest(){}
_this.getObject = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
}
else if (window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
else {
alert("XML HTTP Request support cannot be found!");
return null;
}
};
After creating the object, the second part is somewhat straightforward. We create an _this.open = function(url,title) {
if(title&&this._title) {
/* _title is a private reference to the title layer. */
this._title.innerHTML = title;
}
/*hide pop-up*/
this.hide();
/*open an HTTP request to the url*/
var request = new XHRequest().getObject();
request.open("GET",url);
var eventSource = this;
request.onreadystatechange = function() {
if(request.readyState == 4) {
eventSource.changeContent(request.responseText);
}
eventSource.show();
}
request.send(null);
};
That's all folks! ConclusionIn conclusion, we modeled and created a draggable DHTML pop-up that establishes an HTTP connection to an external file; we did some cross-browser OO coding and (hopefully) we had fun. Happy coding! History
| ||||||||||||||||||||