开发者

Adding event listeners to 'memory objects' in Javascript?

Some background to the problem: Reverse Engineering the DOM, Javascript events & "what's going on"?

I'm writing something that will 'play' with the functionality of google's page preview on live search. Essentially, I'd like to add an event listener to the keyup event that will

  1. query the page preview results (send an ajax request to google)
  2. read that result in memory
  3. add it to your search page in realtime

This will essentially create a live page preview search result would of google.

To be fair, I've actually got about 95% of this working in various components and I'm at the stages of tweaking functionality to make it a bit more user friendly. If you're interested in the whys and hows of what I'm doing, please feel free to check out a my previous blog posts at http://chesser.ca or even look at the 'latest version of the code' at http://chesser.ca/gvs.marklet.0.3.js (currently very buggy as i'm a bit "between functionalities" at the moment.

The bookmarklet code for your browser is here:

 javascript:(function{var head= document.getElementsByTagName('head')[0];var script= document.createElement('script');script.type= 'text/javascript';script.src= 'http://chesser.ca/gvs.marklet.0.3.js';head.appendChild(script);};)();

(the keyup event listener is currently set to off, essentially to make it work in its current state, you run a search on a page with live preview and click the bookmarklet once (to initiate queries), wait a second or two, then click it a second time (to display the page previews)

So I've got some changes to this to make:

In part 1 (for performance) I'm interested in only querying the first two elements. I imagine that will be easy and the only thing stopping me is that I haven't tried it yet (instead of all divs.length, just set it to 2)

function query_current_pages(){
     var all_divs = document.getElementsByTagName('div');
     for (i=0;i < all_divs.length; i++) {
         if (all_divs[i].className == 'vsc') {
             google.vs.ea(all_divs[i]);
         }
     }
 }

The next problem (and the one that I'm a not sure about) is how I can set up an event listener and modify the add_previews function such that the previews are added when the request comes back, instead of the rather ham-fisted approach of looping through everything that is in the google.vs.ha memory space rather I'd like to create something that will listen for the data to be there and then spring into action.

The reason i'm trying to do this is because I think it would be really really neat. That and I've learned a 'mountain' of stuff about coding while doing it.

For those interested in how the page previews get "chucked on the page" here's the function that loops through all the images in memory and put them up.

 function add_previews(){
    c=document.getElementById('ires');
    nli=document.createElement('div');
    cell = 0;
    for(var Obj in google.vs.ha){
        na=document.createElement('a');
        na.href=Obj;

        nd=document.createElement('div');
        cldiv=document.createElement('div');
        cldiv.style.clear = 'both';

        nd.style.width=google.vs.ha[Obj].data.dim[0]+'px';
        nd.style.height=google.vs.ha[Obj].data.dim[1]+'px';
        nd.style.margin = '5px';
        nd.style.padding = '5px';
        nd.style.cssFloat = 'left';
        nd.style.border = '1px solid #999999';

        if (google.vs.ha[Obj].data.tbts.length) {
            nilDiv = document.createElement('div'); 
            for(i = 0; i < google.vs.ha[Obj].data.tbts.length; i++){
                box = google.vs.ha[Obj].data.tbts[i].box;
                newbox = document.createElement('div');
         开发者_运维问答       newbox.className = 'vsb vsbb';
                newbox.style.position = 'relative';
                newbox.style.top = (box.t)+'px';
                newbox.style.left = box.l+'px';
                newbox.style.height = box.h+'px';
                newbox.style.width = box.w+'px';
                nilDiv.appendChild(newbox);
                newtext = document.createElement('div');
                newtext.className = 'vsb vstb';
                newtext.innerHTML = google.vs.ha[Obj].data.tbts[i].txt;
                newtext.style.top = (box.t)+'px';
                newtext.style.position = 'relative';
                nilDiv.appendChild(newtext);
            }
            nilDiv.style.height = '0px';
            nd.appendChild(nilDiv);
        }

        for(i = 0; i < google.vs.ha[Obj].data.ssegs.length; i++){
            ni=document.createElement('img');
            ni.src += google.vs.ha[Obj].data.ssegs[i];
            ni.className+=' vsi';
            na.appendChild(ni);
        }
        nd.appendChild(na);
        nli.appendChild(nd);
    };
    c.insertBefore(nli,c.firstChild);           
 } 

The obvious bit to change in there (with an event listener) is to fix for(var Obj in google.vs.ha){ to be a single google.vs.rs object that is passed in.

If you've lasted this far into the question: thanks for reading :) - Alex

EDIT

As per the discussion below, google.vs.Ga seems to be responsible for querying the data (to which the answer is over-writing the function)

For informational purposes (and fun) here's the .Ga code.

  google.vs.Ga = function (a, b, c) {
        var d = google.vs.b.kfe.kfeHost,
            g = google.vs.Ya(a),
            i = a.getAttribute("sig");
        if (i) {
            var f = google.vs.qa(a);
            if (f) {
                d = [d ? "http://" + d : "", google.vs.b.kfe.kfeUrlPrefix, "&d=", encodeURIComponent(f), "&b=1", "&jsonp=google.vs.r"];
                d.push("&a=");
                d.push(encodeURIComponent(i));
                if (i = a.getAttribute("blobref")) {
                    d.push("&bl=");
                    d.push(i)
                }
                d.push("&rs=");
                i = 0;
                for (var j; j = g[i++];) {
                    d.push(encodeURIComponent(j));
                    i < g.length && d.push("&rs=")
                }
                g = google.vs.m(a) || {
                    ub: a
                };
                g.G = c;
                google.vs.ha[f] = g;
                c = d.join("");
                c = new google.vs.Ia(f, c, function () {
                    google.vs.P(a, h);
                    o(google.vs.k, f)
                });
                b ? p(google.vs.k, c) : q(google.vs.k, c)
            }
        }
    };


The google.vs.ha object is a basic JavaScript object, with key/value pair attributes, and no functions to speak of. That being said, these simple objects are not capable of notifying you when they are changed.

The way I see it, you basically have 2 options:

  • Periodically check google.vs.ha for the data that you are looking for, keeping track of which ones you've already grabbed the images. This could be accomplished with setInterval();

  • Determine which function on the page or in the google. namespace is responsible for doing the work of loading the data. Once you determine where the data is getting loaded, and where, exactly, the google.vs.ha objects are getting updated, you can replace the original function with one of your own crafting that contains an event notification.

For example, if I have a basic function that looks like this:

var Example = function(value){
  var closured = ' world';
  this.value = value;
  this.doSomething = function(){ alert(this.value + closured); };
};

var test = new Example('hello');
test.doSomething(); // will only alert 'hello world';

var oldFunc = test.doSomething;
var notifyMe = function(){ alert('notified'); }; // callback function

// Update previous method to do it's normal thing, but then notify after
test.doSomething = function(){
  oldFunc.apply(this, arguments);
  notifyMe();
};

test.doSomething(); // will alert 'hello world', and then 'notified'

In the above code, we have effectively replaced the old doSomething function with one of our own. This new version still performs it's previous duties (via oldFunc.apply), but will then notify you afterwards.

Note that your new function will only have access to the objects 'public' properties, and not the private elements that are captured by a closure (such as the 'closured' var). I seem to remember somewhere that Google tends to avoid private variables as closures approach, due to the complexities this can add to memory management, so that may not be an issue.

UPDATE: I played around a bit with it on a Google search results page. I ran a fresh search, but before clicking on the preview, I executed in the Chrome console the following:

var old = google.vs.Ga;
var newFunc = function(){
  old.apply(this, arguments);
  console.log(arguments);
};

google.vs.Ga = newFunc; 

And it seemed to be triggered after clicking preview.


The images are stored SINCE they are downloaded, right? Obvious. So the only moment that they can change is an AJAX request.

Therefore you have to run some checking-up everytime an AJAX request succeeds. jQuery has global bindings for successful AJAX calls, so it should be possible even without it.

Now when we know this - the checking-up might start from seeing what was the type of the call by some variables avaliable in the response object, or the XMLHTTPRequest object itself. This could narrow down the number of times you have to call your updater even more.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜