130

Can I make a cross-domain JSONP request in JavaScript without using jQuery or other external library? I would like to use JavaScript itself and then parse the data and make it an object so I could use it. Do I have to use an external library? If not, how can I do it?

informatik01
  • 16,038
  • 10
  • 74
  • 104
Dave
  • 1,301
  • 2
  • 9
  • 3
  • possible duplicate of [Help on making and handling jsonp request usin JAVASCRIPT](http://stackoverflow.com/questions/5907777/help-on-making-and-handling-jsonp-request-usin-javascript) – Matt Ball May 26 '11 at 01:40

12 Answers12

155
function foo(data)
{
    // do stuff with JSON
}

var script = document.createElement('script');
script.src = '//example.com/path/to/jsonp?callback=foo'

document.getElementsByTagName('head')[0].appendChild(script);
// or document.head.appendChild(script) in modern browsers
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 2
    Here's a JSBin that can be used to [fiddle with JSONP](http://jsbin.com/omujex/10/edit) from Wikipedia. It was referenced in [this answer](http://stackoverflow.com/questions/15293680/fetch-random-excerpt-from-wikipedia-javascript-client-only/15293681#15293681). – rkagerer Mar 08 '13 at 14:07
  • 1
    I think it's worth pointing out that the response should be of the form: `foo(payload_of_json_data)`, the idea being that when it gets loaded into the script tag, it calls the foo function with the payload already as a javascript object and no parsing is necessary. – Octopus Sep 19 '14 at 03:32
  • @WillMunn I don't think that's doable with JSONP. It's a hack from the pre-CORS days. What do you need to set headers for? The server specifically needs to accept JSONP requests so it should be set up to serve in a sane manner. – Matt Ball Aug 14 '16 at 21:54
  • you're right, I misread some api documentation, there is a special query parameter to do what i wanted when using jsonp appologies. – Will Munn Aug 15 '16 at 06:53
  • @WillMunn no worries. Glad you were able to sort it out! – Matt Ball Aug 15 '16 at 18:43
38

Lightweight example (with support for onSuccess and onTimeout). You need to pass callback name within URL if you need it.

var $jsonp = (function(){
  var that = {};

  that.send = function(src, options) {
    var callback_name = options.callbackName || 'callback',
      on_success = options.onSuccess || function(){},
      on_timeout = options.onTimeout || function(){},
      timeout = options.timeout || 10; // sec

    var timeout_trigger = window.setTimeout(function(){
      window[callback_name] = function(){};
      on_timeout();
    }, timeout * 1000);

    window[callback_name] = function(data){
      window.clearTimeout(timeout_trigger);
      on_success(data);
    }

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = src;

    document.getElementsByTagName('head')[0].appendChild(script);
  }

  return that;
})();

Sample usage:

$jsonp.send('some_url?callback=handleStuff', {
    callbackName: 'handleStuff',
    onSuccess: function(json){
        console.log('success!', json);
    },
    onTimeout: function(){
        console.log('timeout!');
    },
    timeout: 5
});

At GitHub: https://github.com/sobstel/jsonp.js/blob/master/jsonp.js

sobstel
  • 1,432
  • 16
  • 11
32

What is JSONP?

The important thing to remember with jsonp is that it isn't actually a protocol or data type. Its just a way of loading a script on the fly and processing the script that is introduced to the page. In the spirit of JSONP, this means introducing a new javascript object from the server into the client application/ script.

When is JSONP needed?

It is 1 method of allowing one domain to access/ process data from another in the same page asyncronously. Primarily, it is used to override CORS (Cross Origin Resource Sharing) restrictions which would occur with an XHR (ajax) request. Script loads are not subject to CORS restrictions.

How is it done

Introducing a new javascript object from the server can be implemented in many ways, but the most common practice is for the server to implement the execution of a 'callback' function, with the required object passed into it. The callback function is just a function you have already set up on the client which the script you load calls at the point the script loads to process the data passed in to it.

Example:

I have an application which logs all items in someone's home. My application is set up and I now want to retrieve all the items in the main bedroom.

My application is on app.home.com. The apis I need to load data from are on api.home.com.

Unless the server is explicitly set up to allow it, I cannot use ajax to load this data, as even pages on separate subdomains are subject to XHR CORS restrictions.

Ideally, set things up to allow x-domain XHR

Ideally, since the api and app are on the same domain, I might have access to set up the headers on api.home.com. If I do, I can add an Access-Control-Allow-Origin: header item granting access to app.home.com. Assuming the header is set up as follows: Access-Control-Allow-Origin: "http://app.home.com", this is far more secure than setting up JSONP. This is because app.home.com can get everything it wants from api.home.com without api.home.com giving CORS access to the whole internet.

The above XHR solution isn't possible. Set up JSONP On my client script: I set up a function to process the reponse from the server when I make the JSONP call.:

function processJSONPResponse(data) {
    var dataFromServer = data;
}

The server will need to be set up to return a mini script looking something like "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');" It might be designed to return such a string if something like //api.home.com?getdata=room&room=main_bedroom is called.

Then the client sets up a script tag as such:

var script = document.createElement('script');
script.src = '//api.home.com?getdata=room&room=main_bedroom';

document.querySelector('head').appendChild(script);

This loads the script and immediately calls window.processJSONPResponse() as written/ echo/ printed out by the server. The data passed in as the parameter to the function is now stored in the dataFromServer local variable and you can do with it whatever you need.

Clean up

Once the client has the data, ie. immediately after the script is added to the DOM, the script element can be removed from the DOM:

script.parentNode.removeChild(script);
dewd
  • 4,380
  • 3
  • 29
  • 43
  • 2
    Thanks a lot, this helped me very much with my project. A minor issue: I've received `SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data`. After adding single quotes to the data, everything worked fine, so: `"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"` – Hein van Dyke Jun 08 '17 at 15:51
17

My understanding is that you actually use script tags with JSONP, sooo...

The first step is to create your function that will handle the JSON:

function hooray(json) {
    // dealin wit teh jsonz
}

Make sure that this function is accessible on a global level.

Next, add a script element to the DOM:

var script = document.createElement('script');
script.src = 'http://domain.com/?function=hooray';
document.body.appendChild(script);

The script will load the JavaScript that the API provider builds, and execute it.

sdleihssirhc
  • 42,000
  • 6
  • 53
  • 67
  • 2
    Thanks everyone. Got it shooting out over the internet in search of some data and then I do something with it. I used eval() on the response data which helped me deal with the object - array that it is(I think). {It was a bear figuring the parsing out with my limited brainpower but I finally got the value pulled from it}. Fantastic. – Dave May 26 '11 at 04:30
  • @Dave @Matt Maybe I *am* fuzzy on JSONP, but you shouldn't need `eval` or `parse` or anything. You should get JavaScript that the browser can just execute, right? – sdleihssirhc May 26 '11 at 19:15
  • My bad, sorry for the confusion. Trying to get the thing(value?property?) out of the array was making my head spin(The nesting, callback, array, element, object, string, value, curly brackets, brackets...). I removed my use of eval and still got the property(value?) from the array (object?element?) I wanted. – Dave May 29 '11 at 05:14
16

the way I use jsonp like below:

function jsonp(uri) {
    return new Promise(function(resolve, reject) {
        var id = '_' + Math.round(10000 * Math.random());
        var callbackName = 'jsonp_callback_' + id;
        window[callbackName] = function(data) {
            delete window[callbackName];
            var ele = document.getElementById(id);
            ele.parentNode.removeChild(ele);
            resolve(data);
        }

        var src = uri + '&callback=' + callbackName;
        var script = document.createElement('script');
        script.src = src;
        script.id = id;
        script.addEventListener('error', reject);
        (document.getElementsByTagName('head')[0] || document.body || document.documentElement).appendChild(script)
    });
}

then use 'jsonp' method like this:

jsonp('http://xxx/cors').then(function(data){
    console.log(data);
});

reference:

JavaScript XMLHttpRequest using JsonP

http://www.w3ctech.com/topic/721 (talk about the way of use Promise)

Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
derek
  • 171
  • 1
  • 4
5

I have a pure javascript library to do that https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Take a look at it and let me know if you need any help using or understanding the code.

Btw, you have simple usage example here: http://robertodecurnex.github.com/J50Npi/

Roberto Decurnex
  • 2,514
  • 1
  • 19
  • 28
  • 6
    Your solution was a little too simple for my use-case, so i added multiple request support https://gist.github.com/1431613 – Chad Cache Dec 04 '11 at 23:25
4
/**
 * Loads data asynchronously via JSONP.
 */
const load = (() => {
  let index = 0;
  const timeout = 5000;

  return url => new Promise((resolve, reject) => {
    const callback = '__callback' + index++;
    const timeoutID = window.setTimeout(() => {
      reject(new Error('Request timeout.'));
    }, timeout);

    window[callback] = response => {
      window.clearTimeout(timeoutID);
      window[callback] = null;
      resolve(response.data);
    };

    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
    document.getElementsByTagName('head')[0].appendChild(script);
  });
})();

Usage sample:

const data = await load('http://api.github.com/orgs/kriasoft');
Konstantin Tarkus
  • 37,618
  • 14
  • 135
  • 121
3

I wrote a library to handle this, as simply as possible. No need to make it external, its just one function. Unlike some other options, this script cleans up after itself, and is generalized for making further requests at runtime.

https://github.com/Fresheyeball/micro-jsonp

function jsonp(url, key, callback) {

    var appendParam = function(url, key, param){
            return url
                + (url.indexOf("?") > 0 ? "&" : "?")
                + key + "=" + param;
        },

        createScript = function(url, callback){
            var doc = document,
                head = doc.head,
                script = doc.createElement("script");

            script
            .setAttribute("src", url);

            head
            .appendChild(script);

            callback(function(){
                setTimeout(function(){
                    head
                    .removeChild(script);
                }, 0);
            });
        },

        q =
            "q" + Math.round(Math.random() * Date.now());

    createScript(
        appendParam(url, key, q), function(remove){
            window[q] =
                function(json){
                    window[q] = undefined;
                    remove();
                    callback(json);
                };
        });
}
Fresheyeball
  • 29,567
  • 20
  • 102
  • 164
3

Please find below JavaScript example to make a JSONP call without jQuery:

Also, you can refer my GitHub repository for reference.

https://github.com/shedagemayur/JavaScriptCode/tree/master/jsonp

window.onload = function(){
    var callbackMethod = 'callback_' + new Date().getTime();

    var script = document.createElement('script');
    script.src = 'https://jsonplaceholder.typicode.com/users/1?callback='+callbackMethod;

    document.body.appendChild(script);

    window[callbackMethod] = function(data){
        delete window[callbackMethod];
        document.body.removeChild(script);
        console.log(data);
    }
}
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Mayur Shedage
  • 1,027
  • 2
  • 11
  • 19
0
/**
 * Get JSONP data for cross-domain AJAX requests
 * @private
 * @link http://cameronspear.com/blog/exactly-what-is-jsonp/
 * @param  {String} url      The URL of the JSON request
 * @param  {String} callback The name of the callback to run on load
 */
var loadJSONP = function ( url, callback ) {

    // Create script with url and callback (if specified)
    var ref = window.document.getElementsByTagName( 'script' )[ 0 ];
    var script = window.document.createElement( 'script' );
    script.src = url + (url.indexOf( '?' ) + 1 ? '&' : '?') + 'callback=' + callback;

    // Insert script tag into the DOM (append to <head>)
    ref.parentNode.insertBefore( script, ref );

    // After the script is loaded (and executed), remove it
    script.onload = function () {
        this.remove();
    };

};

/** 
 * Example
 */

// Function to run on success
var logAPI = function ( data ) {
    console.log( data );
}

// Run request
loadJSONP( 'http://api.petfinder.com/shelter.getPets?format=json&key=12345&shelter=AA11', 'logAPI' );
2pha
  • 9,798
  • 2
  • 29
  • 43
Chris Ferdinandi
  • 1,892
  • 4
  • 26
  • 34
  • Why `window.document.getElementsByTagName('script')[0];` and not `document.body.appendChild(…)`? – Sukima Aug 04 '16 at 16:10
  • Shouldn't `logAPI` be set to `null` when done so garbage collection can be performed on it? – Sukima Aug 04 '16 at 16:11
0

Just pasting an ES6 version of sobstel's nice answer:

send(someUrl + 'error?d=' + encodeURI(JSON.stringify(json)) + '&callback=c', 'c', 5)
    .then((json) => console.log(json))
    .catch((err) => console.log(err))

function send(url, callback, timeout) {
    return new Promise((resolve, reject) => {
        let script = document.createElement('script')
        let timeout_trigger = window.setTimeout(() => {
            window[callback] = () => {}
            script.parentNode.removeChild(script)
            reject('No response')
        }, timeout * 1000)

        window[callback] = (data) => {
            window.clearTimeout(timeout_trigger)
            script.parentNode.removeChild(script)
            resolve(data)
        }

        script.type = 'text/javascript'
        script.async = true
        script.src = url

        document.getElementsByTagName('head')[0].appendChild(script)
    })
}
just_user
  • 11,769
  • 19
  • 90
  • 135
-1

If you are using ES6 with NPM, you can try node module "fetch-jsonp". Fetch API Provides support for making a JsonP call as a regular XHR call.

Prerequisite: you should be using isomorphic-fetch node module in your stack.

Rajendra kumar Vankadari
  • 2,247
  • 1
  • 16
  • 16