Post by Jordan on Dec 14, 2010 7:32:53 GMT
I don't think I've written a tutorial before, but I'm going to give it a shot. This tutorial assumes you already know the bare basics of Javascript such as variables and if statements. I'm just going to over how to create a basic Proboards code and then give a few examples.
The first concept that you need to understand is the Document Object Model. The DOM "is a cross-platform and language-independent convention for reprecenting and interacting with objects in HTML, XHTML and XML documents" - Wikipedia. "It defines the logical structure of documents and the way a document is accessed and manipulated" - W3. This convention allows you to easily access all the HTML elements on the page via Javascript which you can then manipulate.
For example, look at my made up XML below. As you can see, elements can be parents, siblings, children, grandchildren etc. It's basically just a family tree.
<parent>
<child>Child contents</child>
<child>
<grandchild>Grandchild contents</grandchild>
</child>
</parent>
So when you want to make a code for Proboards, you need to be able to traverse through all these elements and modify specific ones. The first step in accomplishing this task is to setup a loop so you can visit each element and see if it is one that you want to modify. However, before you can do this you need to understand what arrays are and how they work. You've probably seen the code below in many Proboard codes.
var variable = document.getElementsByTagName("ElementName");
What this does is create an array of references to all the "<ElementName>" on the page. In Javascript, an array is an object that contains a set of objects which can all be accessed through one variable. If you wanted to store 100 test scores, an array would be a good choice for storing the values rather than declaring 100 variables. Strings like the one I am typing right now are stored as arrays internally. The array below is a simple example which contains five integers.
var myArray = [85,93,100,81,90];
To access each element, you use the subscript operator [] with an integer index. As you can see below, the indexes start counting at zero in arrays. The reason for this is because the index reprecents an offset from the array's location in memory. I won't into anymore detail about this, but just know that there is a logical reason for starting at zero. (The == operator means "is equal to".)
myArray[0] == 85
myArray[1] == 93
myArray[2] == 100
myArray[3] == 81
myArray[4] == 90
So, if we wanted to access the XML elements that I posted above (pretending that they are HTML elements), we could do something like this.
// Establish an array of references to all the child elements
var c = document.getElementsByTagName("child");
// The first child element in the array
c[0].innerHTML == "Child contents"
// The first node of the second child element in the array
c[1].firstChild.innerHTML == "Grandchild contents"
// This does the exact same thing as the line above, but
// we are just getting there in a different way by using the
// nextSibling property which all elements have
c[0].nextSibling.firstChild.innerHTML == "Grandchild contents"
// Move up the family tree with the "parentNode" property
c[0].parentNode.nodeName.toLowerCase() == "parent"
Note that the properties such as "firstChild", "nextSibling", and "parentNode" have nothing to do with arrays. They are just properties that every element in the DOM have so you can easily move around.
Now that you have a basic understanding of the DOM and arrays, we are going to move on to what makes computers so powerful. Loops. All a loop does is repeat the same instructions over and over again while some expression is true. The most used loop as you may have noticed is the "for" loop.
The for loop below starts at 0, and iterates up to 9. Notice that the loop iterates 10 times, but since it starts at 0 its last value will be one less than what you are looping to (9). This confuses people a lot, but you'll realize after you get some practice that starting at 0 makes sense, especially with arrays. You don't have to start at 0, however, it just depends on what you are doing with the loop.
// set the variable i to an initial value of 0 (i = 0)
// continue while i is less than ten (i < 10)
// increment i by one after each iteration (i++)
for(i = 0; i < 10; i++) {
// this will print 0123456789
document.write(i);
}
Note that you can have all kinds of instructions in the for loop, it doesn't have to look exactly as I typed it up there. Here are a few other examples.
for(i=1; i < 200; i=i+2)
// loop through odd values
for(;
// infinite loop which you would need to break manually
// with the "break" statement
for(node=document.firstChild; node; node=node.nextSibling)
// loop through all the first level nodes/elements on the page
// this won't loop through any childNodes
// on Proboards this would be the <br /> tag, then first
// <table> tag etc.
Below is a loop that you could actually use on your forum. It establishes an array of references to all the anchor tags on the page, loops through them and changes any which have a URL equal to "/index.cgi" to Google's URL. Notice the expression "a.length" in the for loop. The "length" property of an array is an integer who's value is equal to the amount of items in the array. Also notice that we are looping while i is LESS THAN a.length so we are really looping up to length-1 which is the last element in the array because remember, arrays start counting at 0.
var a = document.getElementsByTagName("a");
for(i = 0; i < a.length; i++) {
// if the anchor who's reference is stored at index i in the array has a url equal to /index.cgi
if(a.href == "/index.cgi") {
// change the url
a.href = "http://google.com";
// now i will increase by one, and then the expression
// i < a.length will be evaluated to see if we should
// continue looping
}
}
Here's how we would find the mini-profile.
// pb_action is a global variable defined by Proboards which tells us
// about the page we are viewing. In this case we are checking to see
// if we are on a thread page
if(pb_action == "display") {
// starting at array element 6 (which is the 7th td element
// on the page), loop until we have reached td.length-1
// which is the last td element on the page. We start at the
// 7th td because we know that the welcome table cells
// don't have mini-profiles in them. ;p
for(x = 6; x < td.length; x++){
// if the cell has a width of 20%, and the next cell has
// a width of 80%, then this should be a mini-profile cell
// there are many other ways you could check
if(td[x].width == "20%" && td[x+1].width == "80%"){
// this is a mini profile cell
}
}
}
The next code is a little more challenging. What it does is center all the contents in the mini-profile above the avatar by extracting all the nodes and inserting them into a division element that has its "textAlign" property set to "center". It then re-inserts this division element back into the mini-profile. It knows it has reached the avatar when it encounters a <center> element because that's what the avatar resides in. It is still there even if the user doesn't have an avatar by the way.
// if we are on a thread page
if(pb_action == "display") {
for(x = 6; x < td.length; x++){
if(td[x].width == "20%" && td[x+1].width == "80%"){
// Create a divison element to store the data
// we are about to extract from the mini-profile
var _div = document.createElement("div");
_div.style.width = "100%";
_div.style.textAlign = "center";
// Get a reference to the second node in the cell
// I didn't select the first node because as you will
// see in the loop below, I push in the previousSibling
// which I explain below. There is no previousSibling for
// the "firstChild".
var _node = td[x].firstChild.nextSibling;
// While the current node's name does NOT
// equal "center" in uppercase. The reason you
// need to convert to uppercase or lower case
// is because different browsers convert the
// names to different cases and the statement is
// case sensitive.
while(_node.nodeName.toUpperCase() != "CENTER"){
// Push the previous node into the div element
// This also removes the node from the mini-profile
// which is exactly why I push the previousNode and
// not the current node because if I appended the current
// node its "nextSibling" would no longer be valid
// and I need that in the next line.
_div.appendChild(_node.previousSibling);
// Get a reference to the next node
_node = _node.nextSibling;
}
// Re-insert the div element back into the cell before the avatar
// which has temporarily become the firstChild of the cell since
// all the other nodes have been removed
td[x].insertBefore(_div, td[x].firstChild);
}
}
}
I don't know if this made any sense so let me know if you have any questions.
The first concept that you need to understand is the Document Object Model. The DOM "is a cross-platform and language-independent convention for reprecenting and interacting with objects in HTML, XHTML and XML documents" - Wikipedia. "It defines the logical structure of documents and the way a document is accessed and manipulated" - W3. This convention allows you to easily access all the HTML elements on the page via Javascript which you can then manipulate.
For example, look at my made up XML below. As you can see, elements can be parents, siblings, children, grandchildren etc. It's basically just a family tree.
<parent>
<child>Child contents</child>
<child>
<grandchild>Grandchild contents</grandchild>
</child>
</parent>
So when you want to make a code for Proboards, you need to be able to traverse through all these elements and modify specific ones. The first step in accomplishing this task is to setup a loop so you can visit each element and see if it is one that you want to modify. However, before you can do this you need to understand what arrays are and how they work. You've probably seen the code below in many Proboard codes.
var variable = document.getElementsByTagName("ElementName");
What this does is create an array of references to all the "<ElementName>" on the page. In Javascript, an array is an object that contains a set of objects which can all be accessed through one variable. If you wanted to store 100 test scores, an array would be a good choice for storing the values rather than declaring 100 variables. Strings like the one I am typing right now are stored as arrays internally. The array below is a simple example which contains five integers.
var myArray = [85,93,100,81,90];
To access each element, you use the subscript operator [] with an integer index. As you can see below, the indexes start counting at zero in arrays. The reason for this is because the index reprecents an offset from the array's location in memory. I won't into anymore detail about this, but just know that there is a logical reason for starting at zero. (The == operator means "is equal to".)
myArray[0] == 85
myArray[1] == 93
myArray[2] == 100
myArray[3] == 81
myArray[4] == 90
So, if we wanted to access the XML elements that I posted above (pretending that they are HTML elements), we could do something like this.
// Establish an array of references to all the child elements
var c = document.getElementsByTagName("child");
// The first child element in the array
c[0].innerHTML == "Child contents"
// The first node of the second child element in the array
c[1].firstChild.innerHTML == "Grandchild contents"
// This does the exact same thing as the line above, but
// we are just getting there in a different way by using the
// nextSibling property which all elements have
c[0].nextSibling.firstChild.innerHTML == "Grandchild contents"
// Move up the family tree with the "parentNode" property
c[0].parentNode.nodeName.toLowerCase() == "parent"
Note that the properties such as "firstChild", "nextSibling", and "parentNode" have nothing to do with arrays. They are just properties that every element in the DOM have so you can easily move around.
Now that you have a basic understanding of the DOM and arrays, we are going to move on to what makes computers so powerful. Loops. All a loop does is repeat the same instructions over and over again while some expression is true. The most used loop as you may have noticed is the "for" loop.
The for loop below starts at 0, and iterates up to 9. Notice that the loop iterates 10 times, but since it starts at 0 its last value will be one less than what you are looping to (9). This confuses people a lot, but you'll realize after you get some practice that starting at 0 makes sense, especially with arrays. You don't have to start at 0, however, it just depends on what you are doing with the loop.
// set the variable i to an initial value of 0 (i = 0)
// continue while i is less than ten (i < 10)
// increment i by one after each iteration (i++)
for(i = 0; i < 10; i++) {
// this will print 0123456789
document.write(i);
}
Note that you can have all kinds of instructions in the for loop, it doesn't have to look exactly as I typed it up there. Here are a few other examples.
for(i=1; i < 200; i=i+2)
// loop through odd values
for(;
// infinite loop which you would need to break manually
// with the "break" statement
for(node=document.firstChild; node; node=node.nextSibling)
// loop through all the first level nodes/elements on the page
// this won't loop through any childNodes
// on Proboards this would be the <br /> tag, then first
// <table> tag etc.
Below is a loop that you could actually use on your forum. It establishes an array of references to all the anchor tags on the page, loops through them and changes any which have a URL equal to "/index.cgi" to Google's URL. Notice the expression "a.length" in the for loop. The "length" property of an array is an integer who's value is equal to the amount of items in the array. Also notice that we are looping while i is LESS THAN a.length so we are really looping up to length-1 which is the last element in the array because remember, arrays start counting at 0.
var a = document.getElementsByTagName("a");
for(i = 0; i < a.length; i++) {
// if the anchor who's reference is stored at index i in the array has a url equal to /index.cgi
if(a.href == "/index.cgi") {
// change the url
a.href = "http://google.com";
// now i will increase by one, and then the expression
// i < a.length will be evaluated to see if we should
// continue looping
}
}
Here's how we would find the mini-profile.
// pb_action is a global variable defined by Proboards which tells us
// about the page we are viewing. In this case we are checking to see
// if we are on a thread page
if(pb_action == "display") {
// starting at array element 6 (which is the 7th td element
// on the page), loop until we have reached td.length-1
// which is the last td element on the page. We start at the
// 7th td because we know that the welcome table cells
// don't have mini-profiles in them. ;p
for(x = 6; x < td.length; x++){
// if the cell has a width of 20%, and the next cell has
// a width of 80%, then this should be a mini-profile cell
// there are many other ways you could check
if(td[x].width == "20%" && td[x+1].width == "80%"){
// this is a mini profile cell
}
}
}
The next code is a little more challenging. What it does is center all the contents in the mini-profile above the avatar by extracting all the nodes and inserting them into a division element that has its "textAlign" property set to "center". It then re-inserts this division element back into the mini-profile. It knows it has reached the avatar when it encounters a <center> element because that's what the avatar resides in. It is still there even if the user doesn't have an avatar by the way.
// if we are on a thread page
if(pb_action == "display") {
for(x = 6; x < td.length; x++){
if(td[x].width == "20%" && td[x+1].width == "80%"){
// Create a divison element to store the data
// we are about to extract from the mini-profile
var _div = document.createElement("div");
_div.style.width = "100%";
_div.style.textAlign = "center";
// Get a reference to the second node in the cell
// I didn't select the first node because as you will
// see in the loop below, I push in the previousSibling
// which I explain below. There is no previousSibling for
// the "firstChild".
var _node = td[x].firstChild.nextSibling;
// While the current node's name does NOT
// equal "center" in uppercase. The reason you
// need to convert to uppercase or lower case
// is because different browsers convert the
// names to different cases and the statement is
// case sensitive.
while(_node.nodeName.toUpperCase() != "CENTER"){
// Push the previous node into the div element
// This also removes the node from the mini-profile
// which is exactly why I push the previousNode and
// not the current node because if I appended the current
// node its "nextSibling" would no longer be valid
// and I need that in the next line.
_div.appendChild(_node.previousSibling);
// Get a reference to the next node
_node = _node.nextSibling;
}
// Re-insert the div element back into the cell before the avatar
// which has temporarily become the firstChild of the cell since
// all the other nodes have been removed
td[x].insertBefore(_div, td[x].firstChild);
}
}
}
I don't know if this made any sense so let me know if you have any questions.