3

How do you use AJAX (in plain JavaScript, NOT jQuery) to get a page (same domain) and display just a specific DOM Element? (Such as the DOM element marked with the id of "bodyContent").

I'm working with MediaWiki 1.18, so my methods have to be a little less conventional (I know that a version of jQuery can be enabled for MediaWiki, but I don't have access to do that so I need to look at other options). I apologize if the code is messy, but there's a reason I need to build it this way. I'm mostly interested in seeing what can be done with the Javascript.

Here's the HTML code:

<div class="mediaWiki-AJAX"><a href="http://www.domain.com/whatever"></a></div>

Here's the Javascript I have so far:

var AJAXs = document.getElementsByClassName('mediaWiki-AJAX');
if (AJAXs.length > 0) {
    for (var i = AJAXs.length - 1; i >= 0; i--) {
        var URL = AJAXs[i].getElementsByTagName('a')[0].href;
        xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                AJAXs[i].innerHTML = xmlhttp.responseText;
            }
        }
        xmlhttp.open('GET',URL,true);
        xmlhttp.send();
    }
}


EDIT:

Thanks to the answer lbstr gave, I was able to come up with the following, which works! You'll note that I added in extra steps to remove any elements that I didn't want. For whatever reason, getElementById() didn't work on the responseText, but getElementsByClassName() did. I imagine it's because the ID of "bodyContent" already exists on the wiki page prior to the AJAX call being run.

//This function is necessary to read the response text correctly.
function outerHTML(node){
    // if IE, Chrome take the internal method otherwise build one
    return node.outerHTML || (
    function(n){
        var div = document.createElement('div'), h;
        div.appendChild( n.cloneNode(true) );
        h = div.innerHTML;
        div = null;
        return h;
    })(node);
}

// This code deals with populating the AJAX divs within a wiki page.
var AJAXs = document.getElementsByClassName('mediaWiki-AJAX');
if (AJAXs.length > 0) {
    for (var i = AJAXs.length - 1; i >= 0; i--) {
        (function(index){
            var URL = AJAXs[index].getElementsByTagName('a')[0].href;
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.onreadystatechange = function() {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    var tempDiv = document.createElement('div');
                    tempDiv.innerHTML = xmlhttp.responseText;
                    var AJAXContent = tempDiv.getElementsByClassName('mw-content-ltr')[0];

                    //Remove unnecessary "fixed" items to avoid clutter.
                    var hiddenItems = AJAXContent.getElementsByClassName('hidden-item');
                    if (hiddenItems.length > 0) {
                        for (var j = hiddenItems.length - 1; j >= 0; j--) {
                            AJAXContent.removeChild(hiddenItems[j]);
                        }
                    }
                    var protectedItems = AJAXContent.getElementsByClassName('protected-item');
                    if (protectedItems.length > 0) {
                        for (var j = protectedItems.length - 1; j >= 0; j--) {
                            AJAXContent.removeChild(protectedItems[j]);
                        }
                    }

                    //Insert the AJAX content and rerun Javascript on it.
                    if (AJAXContent !== null && AJAXContent !== undefined) {
                        AJAXs[index].innerHTML = outerHTML(AJAXContent);
                        assign_Popup_Functions(AJAXs[index]);
                        fix_External_Links(AJAXs[index]);
                        assign_iFrame_Functions(AJAXs[index]);
                    }
                }
            }
            xmlhttp.open('GET',URL,true);
            xmlhttp.send();
        })(i);
    }
}
Community
  • 1
  • 1
Matt Frost
  • 55
  • 9
  • I think jQuery would make your life easier and you don't need any access to enable it. In the worst case, you can just copy paste the minified jQuery at the beginning of your script. If you're worried about size, you can even take the unminified jQuery and pick only the pieces you're interested in, then minify that and use it. The answer below seems sensible but jQuery is always going to be more elegant and future-proof. – Milimetric Nov 19 '12 at 17:20

2 Answers2

4

it seems like in your callback you could just do this:

var temp = document.createElement("div");
temp.innerHTML = xmlhttp.responseText;
var bodyContent = temp.getElementById("bodyContent");
if (bodyContent !== null && bodyContent !== undefined) {
    AJAXs[i].innerHTML = outerHTML(bodyContent);
}

Given this outerHTML function from this SO post:

function outerHTML(node){
    // if IE, Chrome take the internal method otherwise build one
    return node.outerHTML || (
    function(n){
        var div = document.createElement('div'), h;
        div.appendChild( n.cloneNode(true) );
        h = div.innerHTML;
        div = null;
        return h;
    })(node);
}

Keep in mind that if you are doing this to multiple AJAXs containers, you could end up with multiple items wiht the same ID (bodyContent). Make sure you come up with some way to avoid that. Maybe you want innerHTML instead?

EDIT:

I also forgot to point out a bug that will arise with your setup. Since your callback will be reached after the loop is done, the value of i will always be 0, so you won't update the correct container. You'll need to do something like this:

for (var i = AJAXs.length - 1; i >= 0; i--) {
    (function(index){
        var URL = AJAXs[index].getElementsByTagName('a')[0].href;
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                AJAXs[index].innerHTML = xmlhttp.responseText;
            }
        }
        xmlhttp.open('GET',URL,true);
        xmlhttp.send();
    })(i);
}
Community
  • 1
  • 1
lbstr
  • 2,822
  • 18
  • 29
  • I don't think your edit is correct. You need to declare xmlhttp as a local variable. – Christophe Nov 20 '12 at 04:49
  • @Christophe I didn't introduce the `xmlhttp` variable; I'm just going off of the OP. I'm guessing that the variable has been declared. If not, you're right, it should be declared. I'm not sure that I understand how that makes my edit incorrect though. – lbstr Nov 20 '12 at 16:34
  • The edit works if you add a "var" in front of the "xmlhttp = new XMLHttpRequest();". Now I just gotta figure out how to incorporate the other information you provided... thanks btw! – Matt Frost Nov 20 '12 at 16:43
  • @Christophe I'm sorry, you were right! if you don't declare it as a local, you'll run into the same problem as before with `i`. Thank you for pointing that out. – lbstr Nov 20 '12 at 17:10
0

An iframe is a convenient way to do this. Here are the high level steps:

  1. Create a hidden iframe
  2. Load the remote url
  3. onload access the iframe content
  4. get your specific element

An iframe will be especially useful if your remote page includes JavaScript that modifies the page content (a simple ajax request wouldn't run the scripts).

This is definitely a situation where a library like jQuery really helps, as it'll make everything transparent for you.

Christophe
  • 27,383
  • 28
  • 97
  • 140
  • this is definitely a viable solution, but I would argue that it would not perform very well. – lbstr Nov 20 '12 at 16:35
  • @lbstr it all depends on your page content (pictures in particular). I insist that a simple GET request is ok for a static page, but not enough for a dynamic page. – Christophe Nov 20 '12 at 16:45