gael
gael

Reputation: 55

simple JSONP & PHP example not working

Been trying to create a simple JSONP call but it doesn't always work and I dunno why. Here's the code:

server-side (http://server/server.php):

<?php
    $res = json_encode("It works!");

    if(isset($_GET['callback']) === TRUE) {
        header('Content-Type: text/javascript;');
        header('Access-Control-Allow-Origin: http://client');
        header('Access-Control-Max-Age: 3628800');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
        echo $_GET['callback']."(".$res.");";
    } else {
        echo $res;
    }
?>

client-side (http://client/client.html):

<html>
    <head><title>JSONP</title></head>
    <body>
        <h1>JSONP Experiment</h1>
        <script src="http://code.jquery.com/jquery-latest.min.js"></script>
        <script>
            function process(data) {
                $('#result').text(data);
            }

            $.getJSON(
                'http://server/server.php?callback=?',
                {'callback': 'process'}
            );

        </script>
        <p id="result"></p>

    </body>
</html>

This code works and displays "It works!" in my

block.

Both not-working cases actually return process("It works"); but this isn't executed, why?

Thanks

Upvotes: 3

Views: 920

Answers (1)

DaveRandom
DaveRandom

Reputation: 88707

The are two different issues here, with different answers


The reason it doesn't work when you use $.getJSON() and put the name of the callback literally in the URL is because of the way in which jQuery works.

Firstly lets look at how jQuery detects that your $.getJSON() call is expecting JSONP if you don't pass any options that indicate it explicitly in the settings object - it uses this regular expression:

/(=)\?(?=&|$)|\?\?/

This looks explicitly for either =? in the query string or a query string consisting of nothing but a single ? - in essence, the question mark is required for detecting that the URL will return JSONP.

Without it, jQuery makes an Ajax request using XHR and the correct data is returned by the server. What happens next depends on the Same Origin Policy. If, like the code you show, the server indicates via Access-Control-* headers that the request is allowed, the data will be accessible just liek it would be with an Ajax request to the client's origin server. If these headers are not present, the data returned will not be accessible to the client code.

But, crucially, because it just makes a standard Ajax request and does not add a <script> element to the DOM, it means that the response text is never evaluated as Javascript code - the crucial last-step in the JSONP mechanism.


The second version is a little trickier to answer and know that the answer is correct with the information provided, but with the HTML layout you show above I'm reasonably confident this is the reason: Your HTML elements are defined in the wrong order.

When processing the DOM on page load, processing halts at every <script> element while the associated Javascript is synchronously loaded and processed. This is to ensure that all Javascript is executed in the intended order, and that any calls to things which directly modify the DOM in a location specific way - such as document.write() (which you should never use, in case you don't know) - are honoured correctly.

The upshot of this is that if you placed the <script> element in the DOM before the <p id="result"> tag, the result tag doesn't actually exist at the point where the process() function is called. And because you used a jQuery selector instead of document.getElementById(), it swallows the error that would have resulted if you had tried to modify it directly.

This is the reason that many Javascript developers (including myself) will now tell you that you should place your <script> tags as the very last elements in the <body>, as it gives a perceived performance improvement - even though in reality it takes exactly the same amount of network time to load and process all the page resources, it allows the browser to render the page quicker. Doing this also negates the need to use things like $(document).ready()/DOMContentLoaded events to delay execution (which, incidentally, would also solve this problem, but in a slightly messier way).

Upvotes: 5

Related Questions