• Steam recently changed the default privacy settings for all users. This may impact tracking. Ensure your profile has the correct settings by following the guide on our forums.

[Tutorial] Greasemonkey: Adding Ajax to a static task

Chathurga

Active Member
In this tutorial I will guide you through taking a task that is not powered by Ajax and then making it Ajax enabled. I'll be working with the MfM front page to add a little link to show related posts without having to go into the article's page. Not a very useful feature but it's just for the sake of the tutorial.
Before you start this tutorial you'll need to know a little about programming in general and although knowing some javascript will help (since GM scripts are essentially JS) it is not really need as Javascript is very easy to learn. You will also probably need some HTML knowledge but, again, not much.

For those not in the know, Greasemonkey is an extension for the popular browser Firefox 3 to run user made scripts and you will need both of them to use tutorial, sorry to those of you not using FF. Opera and IE can offer Greasemonkey like abilities but since I'll be using the GM API you will need the real thing.
Firebug will also greatly help you for this tutorial and web development in general. I will be locating elements on the page with it so I really recommend getting it.

Right let's get started, install FF then GM. After you restart Firefox you should see a little monkey in the lower right hand side of the Firefox window. Open up the MfM Front Page, right-click on the monkey face and choose "New User Script...". Use "MfM: Related Posts" as the name and enter your name in the Namespace section. Make sure the Includes section has "http://www.mformature.net/" in it, the other sections can be left blank. Click Ok. GM should then prompt you to select what ever text editor you want to make the script in, if you don't have a preference then just use notepad (usually C:\WINDOWS\NOTEPAD.EXE).

Your editor should pop up with something like this already in the script:
[highlight=javascript]// ==UserScript==
// @name MfM: Related Posts
// @namespace Blarg
// @include *mformature.net/
// ==/UserScript==[/highlight]
I only added the * wildcard as vB kept grabbing the title for the link, wrecking the code. Whatever you have is fine (although what I have will work too).
Leave all that there, that's the script metadata and it's needed.

Now to the actual coding part, for reference I will be adding the new link here:

img1y.gif


In order to add another link beside an original one we will need to know where in the page we are going to be making additions. This means finding a sort of unique anchor to the location and while this is easy to do in wordpress (what powers the front page), on most other sites this can be a far more difficult task.
So, while on the MfM front page click Firebug's little bug icon (also in the bottom right corner). A panel should have popped up from below, this can show the HTML structure of a page and I cannot stress enough how amazingly useful this feature is. Anyway click the "Inspect" button in the top left corner of this panel and then click on the "Full Page Article" box (not the actual link) you can see in the previous image.
Firebug should have focused on a span element like so:

img2b.gif


See how it has a class attribute of "topMore"? This is the unique anchor we need as, luckily, all full article links (and only those links) have that class which will make it very easy to find them.
Now that we have somewhere to place the link and some way of finding that place go back into your editor and make a new line past the metadata.
We will now be making use of a relatively new function that was added in FF3, getElementsByClassName which will make our job a lot easier, let me show you:
[highlight=javascript]// Loop through all the elements with topMore as their class and then turn them green
// I will call these elements "el"
// document refers to the current page
for (i=0; (el = document.getElementsByClassName('topMore')); i++) {
el.style.backgroundColor = "green";
}[/highlight]
This "for loop" basically just cycles through all the elements with that class one by one and then code inside the curly brackets will be executed for each one.
The style.backgroundColor part is pretty self explanatory, it just changes an element's background color (in this case to green). For those who know CSS take note that when you change an attribute which contains a dash you will need to remove the dash and make the letter after it a capitol.
e.g. background-color turns into backgroundColor

After you've added that code, click save in your editor and refresh the front page (GM script only execute once and that's at page load so you will need to refresh to see changes). With any luck you'll now see that all the boxes look like this:

img3.gif


Hooray! Now for the slightly more complicated part, adding the new link. Now there's a few things you'll need to know before I start this part.
  1. All attributes of a element can be accessed like I did with style.
    e.g. el.id, el.href (for links), el.title, etc. To access class you need to use el.className instead.
  2. You can access elements around the element you're focused on by using certain methods.
    el.parentNode refers to the element that holds el.
  3. There are a few functions to make new elements and insert them into the page:
    document.createElement does what you'd imagine, creates a new element.
    appendChild gets an element and inserts it into the page.

Onto the code, remove what we did a second ago and use this:
[highlight=javascript]// I will fill in this function in the next step
function ajaxRelated() {}

for (i=0; (el = document.getElementsByClassName('topMore')); i++) {
// create a new 'a' tag, call it link for now
link = document.createElement('a');
// get the link to the full page article, this is the url we will access using ajax to get the related posts
// we will get it by accessing the href attribute of the existing link using the getElementsByTagName function
// [0] means to use the first 'a' tag found inside el, since there will only be one this is okay
url = el.getElementsByTagName('a')[0].href;
// set links title to the url, so it can be found easier the next time it is needed
link.title = url;
// use innerHTML to set the link's text
// innerHTML can set HTML inside an element e.g. el.innerHTML = '<p>blarg</p>' would wipe out everything inside el and change it to a 'p' tag conatining the word blarg
// it is not standards complient and although I hate breaking standards, the function is just too useful not to use
link.innerHTML = "Related Posts";
// in the next section I will be making a new function to use when a user clicks a button but we can't use the onclick attribute as it can't access fucntions defined in a GM script
// we will instead use addEventListener to tie the action of clicking the link to grabbing the related posts
// the first parameter is what action should trigger the function, the second is the function name. You can ignore the third parameter, always put false
link.addEventListener('click',ajaxRelated,false);
// insert the link into the page under the same parent as el
el.parentNode.appendChild(link);
}[/highlight]

When you finish reading over that and have inserted it into the script, refresh and you should see something like this:

img4m.gif



Not very pretty but it'll do for now. You'll notice that clicking the link does nothing yet since the ajaxRelated function is empty, time to fill it in.
We will be using GM_xmlhttpRequest to fetch the full page, this function is part of the Greasemonkey API and does not exist in Javascript. The main difference between GM_xmlhttpRequest and xmlhttpRequest (which does exist in Javascript) is that the GM function call all URLs where the JS version can only call pages from the domain you are on.
e.g. GM can call www.yahoo.com from www.google.com but JS cannot.
And just before we get to the code, open up one of the full articles and use Firebug to get the class name of the related posts area. If you're having trouble getting it then the class name is "related_posts".
The function:
[highlight=javascript]function ajaxRelated() {
// this refers to whatever element called the function
el = this;
// get the url that's stored in the link's title
url = el.title;
// we will now fetch the full page using GM_xmlhttpRequest
GM_xmlhttpRequest({
// method can be either GET or POST, we do not need to post anything so we will use GET
method: "GET",
// we already have the url so we can just use that
url: url,
// this is the function that will be called when the page has loaded, we will use the response to cut out the related posts and paste them in the front page
// ignore the o, it is just needed to get the response in plain text
onload: function(o) {
// the response, it is HTML
response = o.responseText;
// create a temporary element to store the response and format it into something we can access using Javascript. We cannot access the elements while they are in plain text
temp = document.createElement('p');
// we will use the slice function to obtain everything contained in the response's body tag and to remove all the rest
// the method I use is slightly complicated so you can ignore it, it will almost always work so you don't need to understand it yet
body = response.slice(response.indexOf(">", response.indexOf('<body'))+1,response.indexOf("</body>"));
// set the innerHTML of the temporary element to the body of the full page. We can now access the elements properly
temp.innerHTML = body;
// we can now use temp to access the element with a class of "related_posts"
// since there is only one, [0] will be it
related = temp.getElementsByClassName('related_posts')[0];
// now insert it into the front page
el.parentNode.appendChild(related);
// remove el as it is no longer needed
el.parentNode.removeChild(el);
// done!
}
});
}[/highlight]

Now insert, save and refresh the page. Click the link and this should happen (remember to give it a second as it has to fetch the new page):

img5.gif


Aaaaaaand we're done! hope you found this tutorial useful. If you have any questions or problems or the like then ask away, I may write more tutorials like this if anyone wants them.

Here's the full script code (without comments):
[highlight=javascript]// ==UserScript==
// @name Related Posts
// @namespace :D
// @include *mformature.net/
// ==/UserScript==

function ajaxRelated() {
el = this;
url = el.title;
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(o) {
response = o.responseText;
temp = document.createElement('p');
body = response.slice(response.indexOf(">", response.indexOf('<body'))+1,response.indexOf("</body>"));
temp.innerHTML = body;
related = temp.getElementsByClassName('related_posts')[0];
el.parentNode.appendChild(related);
el.parentNode.removeChild(el);
}
});
}

for (i=0; (el = document.getElementsByClassName('topMore')); i++) {
link = document.createElement('a');
url = el.getElementsByTagName('a')[0].href;
link.title = url;
link.innerHTML = "Related Posts";
link.addEventListener('click',ajaxRelated,false);
el.parentNode.appendChild(link);
}[/highlight]
 

EvilSeph

Administrator
Nice work Chat! Who knows, maybe someone will come up with a cool feature like your console filter greasemonkey script and we'll like it enough to grab it and implement it on the site ;)

Your editor should pop up with something like this already in the script:
[highlight=javascript]// ==UserScript==
// @name MfM: Related Posts
// @namespace Blarg
// @include *mformature.net/
// ==/UserScript==[/highlight]
I only added the * wildcard as vB kept grabbing the title for the link, wrecking the code. Whatever you have is fine (although what I have will work too).

This should be fixed now :)

[highlight=javascript]// ==UserScript==
// @name MfM: Related Posts
// @namespace Blarg
// @include http:///www.mformature.net/
// ==/UserScript==[/highlight]
 
Top