JavaScript: JSONP Function Scope Problem

Dear Experts

(NB: Jquery is not an option)

The following code:

1. Attaches a div tag
2. Attaches a script tag
3. Sends a JSONP request

When the callback returns, tp2 is traced and not tp1 as desired.

The reason for this seems to be that the callback is returned outside of the scope of the function. I want to keep the code within the function block to avoid name conflicts when minified.

Can anybody think of a simple solution?

(function() {
    window.onload = init;
    window.setInterval(updateData, 1500);
    var k = '';

    function init() {
        appendDiv();
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn');
    }

    function appendDiv() {
        var at = document.createElement('div');
        at.id = '__atd';
        at.style.position = "absolute";
        at.style.left = "-5000px";
        at.style.top = "-5000px";
        var t = document.getElementsByTagName('body')[0];
        t.appendChild(at);
    }

    function addScriptTag(src) {
        document.getElementById("__atd").innerHTML = '';
        var at = document.createElement('script');
        at.id = '__atj';
        at.type = 'text/javascript';
        at.async = true;
        at.src = src;
        var t = document.getElementById('__atd');
        t.appendChild(at);
    }

    function handleReturn(obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

    function sendInitData() {  //Init State 
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }
})();

function handleReturn(obj) {
    console.log('tp2');
    console.log(obj);
} 

Open in new window

AdrianSmithUKAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

RobOwner (Aidellio)Commented:
Can you not just assign the function to the window:

(function() {
...
window.handleReturn = function(obj) {
...
}

...
})();
0
leakim971PluritechnicianCommented:
Use a namespace :
addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=com.mydomain.handleReturn');

With the following :

(function() {
    window.onload = init;
    window.setInterval(updateData, 1500);
    var k = '';

    function init() {
        appendDiv();
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=com.mydomain.handleReturn');
    }

    function appendDiv() {
        var at = document.createElement('div');
        at.id = '__atd';
        at.style.position = "absolute";
        at.style.left = "-5000px";
        at.style.top = "-5000px";
        var t = document.getElementsByTagName('body')[0];
        t.appendChild(at);
    }

    function addScriptTag(src) {
        document.getElementById("__atd").innerHTML = '';
        var at = document.createElement('script');
        at.id = '__atj';
        at.type = 'text/javascript';
        at.async = true;
        at.src = src;
        var t = document.getElementById('__atd');
        t.appendChild(at);
    }

    function handleReturn(obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

   window["com"] = window["com"] || {};
   window["com"]["mydomain"] = window["com"]["mydomain"] || {};
   window["com"]["mydomain"]["handleReturn"] = handleReturn;

    function sendInitData() {  //Init State 
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }
})();

function handleReturn(obj) {
    console.log('tp2');
    console.log(obj);
} 

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
AdrianSmithUKAuthor Commented:
Hi TagIt

I did think about using

window.handleReturn

but once I put it through the minifier it might come out as

w.h

and there could be hundreds of w.h's floating around.
0
Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

AdrianSmithUKAuthor Commented:
Hi Leakim971

I love your idea but I'm having a problem getting it to run.

I modified the code as you suggested (the two bottom urls also needed changing) and I still get tp2.

Any ideas?

(function() {
    window.onload = init;
    window.setInterval(updateData, 1500);
    var k = '';

    function init() {
        appendDiv();
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn');
    }

    function appendDiv() {
        var at = document.createElement('div');
        at.id = '__atd';
        at.style.position = "absolute";
        at.style.left = "-5000px";
        at.style.top = "-5000px";
        var t = document.getElementsByTagName('body')[0];
        t.appendChild(at);
    }

    function addScriptTag(src) {
        document.getElementById("__atd").innerHTML = '';
        var at = document.createElement('script');
        at.id = '__atj';
        at.type = 'text/javascript';
        at.async = true;
        at.src = src;
        var t = document.getElementById('__atd');
        t.appendChild(at);
    }

    function handleReturn(obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

   window["com"] = window["com"] || {};
   window["com"]["xyz"] = window["com"]["xyz"] || {};
   window["com"]["xyz"]["handleReturn"] = handleReturn;

    function sendInitData() {  //Init State 
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn&k=' + k);
    }
})();

function handleReturn(obj) {
    console.log('tp2');
    console.log(obj);
} 

Open in new window

0
RobOwner (Aidellio)Commented:
Then look at what @leakim971 has suggested.  using a namespace should reduce that possibility and is the same as what I suggested with a little more detail:

window.com = {};
window.com.mydomain = {};
window.com.mydomain.handleReturn = handleReturn;

Open in new window

0
RobOwner (Aidellio)Commented:
Does the script that's returns from your jsonp call window.com.xyz.handleReturn?  Can we see the section of the code for the callback?
0
leakim971PluritechnicianCommented:
be sure to use the latest code (caching problem)

else you can do this too if you don't need to reuse the function :

   window["com"] = window["com"] || {};
   window["com"]["xyz"] = window["com"]["xyz"] || {};
   window["com"]["xyz"]["handleReturn"] =     function (obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

Open in new window

0
AdrianSmithUKAuthor Commented:
Hi LeaKim971

In your first code posting the urls are as follows. Is this correct?

function sendInitData() {  //Init State 
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=handleReturn&k=' + k);
    }

Open in new window

0
RobOwner (Aidellio)Commented:
Something is going amiss here.  I'm using this code:

<!DOCTYPE html>
<html>
<head>
<title></title>
<script>
(function() {
    window.onload = init;
    window.setInterval(updateData, 1500);
    var k = '';

    function init() {
        appendDiv();
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=window.com.xyz.handleReturn');
    }

    function appendDiv() {
        var at = document.createElement('div');
        at.id = '__atd';
        at.style.position = "absolute";
        at.style.left = "-5000px";
        at.style.top = "-5000px";
        var t = document.getElementsByTagName('body')[0];
        t.appendChild(at);
    }

    function addScriptTag(src) {
        document.getElementById("__atd").innerHTML = '';
        var at = document.createElement('script');
        at.id = '__atj';
        at.type = 'text/javascript';
        at.async = true;
        at.src = src;
        var t = document.getElementById('__atd');
        t.appendChild(at);
    }

	window.com = {};
	window.com.xyz = {};
    window.com.xyz.handleReturn = function(obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

    function sendInitData() {  //Init State
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=window.com.xyz.handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.abc.com/t/Library/ping-jsonp.php?callback=window.com.xyz.handleReturn&k=' + k);
    }
})();


function handleReturn(obj) {
    console.log('tp2');
    console.log(obj);
}

</script>
</head>

<body bgcolor="#ffffff" text="#000000" link="#ff0000" vlink="#800000" alink="#ff00ff" background="?">

</body>
</html>

Open in new window


I can call both functions separately and get both tp1 and tp2 output to the console:
So I suspect you need to check the callback javascript
console
0
RobOwner (Aidellio)Commented:
Obviously I can't test the callbacks but I can call both handleReturn functions when the namespace is used as indicated in the screenshot above
0
AdrianSmithUKAuthor Commented:
Hi TagIt

Notice the two URLs on lines 59 and 63.

Is it that?
0
leakim971PluritechnicianCommented:
Is this correct?

Open in new window


no you must specify the function including its namespace : com.xyz.handleReturn

ID: 39470041, line 8, the code is fine
0
RobOwner (Aidellio)Commented:
To get to the bottom of this I'm going to need to know what is returned by this URL:

http://www.abc.com/t/Library/ping-jsonp.php

It obviously returns javascript but until I see it I'm only guessing at what the problem could be or how it is using the callback paramter
0
AdrianSmithUKAuthor Commented:
Hi LeaKim971

Are lines 56 and 60 in your original posting correct though? They are different.

(or lines 59 and 63 in TagIt's last post)
0
RobOwner (Aidellio)Commented:
In reference to your comment above #a39470087 I realised as such the namespace was incorrect and made a few quick edits that you may have missed.
0
AdrianSmithUKAuthor Commented:
Hi TagIt

function getReturnObject($key, $status){
    return 'handleReturn({"k":"'.$key.'","s":"'.$status.'"})';
}

Open in new window


I might need to add the namespace to it. I'll check and let you know.
0
leakim971PluritechnicianCommented:
yes, I forget theses lines, you need to add the namespace too of course!
0
RobOwner (Aidellio)Commented:
yes they should all have callback=window.com.xyz.handleReturn though i'm unsure without seeing the callback code whether the window part needs to be there
0
RobOwner (Aidellio)Commented:
yes you'll need to add the namespace to that!

ie

function getReturnObject($key, $status){
    return 'window.com.xyz.handleReturn({"k":"'.$key.'","s":"'.$status.'"})';
}

Open in new window

0
RobOwner (Aidellio)Commented:
Though i'm confused as to why you specify the callback in the url if it isn't being used?
0
AdrianSmithUKAuthor Commented:
Hurray!!!!

Thanks a million chaps.

Final Solution:

.js
(function() {
    
    window.onload = init;
    window.setInterval(updateData, 1500);
    var k = '';

    function init() {
        appendDiv();
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn');
    }

    function appendDiv() {
        var at = document.createElement('div');
        at.id = '__atd';
        at.style.position = "absolute";
        at.style.left = "-5000px";
        at.style.top = "-5000px";
        var t = document.getElementsByTagName('body')[0];
        t.appendChild(at);
    }

    function addScriptTag(src) {
        document.getElementById("__atd").innerHTML = '';
        var at = document.createElement('script');
        at.id = '__atj';
        at.type = 'text/javascript';
        at.async = true;
        at.src = src;
        var t = document.getElementById('__atd');
        t.appendChild(at);
    }

    function handleReturn(obj) {

        console.log('tp1');
        console.log(obj);

        if (obj.s === 'f') {
            k = obj.k;
            sendInitData();
        }

        else if (obj.s === 'e') {
            k = obj.k;
        }

        else if (obj.s === 'u') {
            k = obj.k;
        }
    }

   window["com"] = window["com"] || {};
   window["com"]["xyz"] = window["com"]["xyz"] || {};
   window["com"]["xyz"]["handleReturn"] = handleReturn;

    function sendInitData() {  //Init State 
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn&k=' + k);
    }

    function updateData() { //Update State
        addScriptTag('http://www.xyz.com/t/Library/ping-jsonp.php?callback=com.xyz.handleReturn&k=' + k);
    }
})();

function handleReturn(obj) {
    console.log('tp2');
    console.log(obj);
} 

Open in new window


.php

function getReturnObject($key, $status){
    return 'com.xyz.handleReturn({"k":"'.$key.'","s":"'.$status.'"})';
}

Open in new window

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.