Master JSONP: Build a Cross‑Domain Hack Before CORS Era
This article explains the historical background of JSONP as a clever workaround for same‑origin restrictions, walks through its core concept and dialogue, provides a complete vanilla JavaScript implementation with usage examples, and discusses its limitations compared to modern CORS solutions.
When we talk about front‑end "cross‑origin" today, CORS (Cross‑Origin Resource Sharing) and the Access‑Control‑Allow‑Origin header dominate the conversation.
Before CORS became a standard, the web lived in a "stone age" where browsers strictly enforced the Same‑Origin Policy, blocking AJAX requests across different origins.
To break through that wall, developers invented a clever, albeit hacky, technique called JSONP (JSON with Padding).
JSONP’s Core Idea: A Beautiful Misunderstanding
The Same‑Origin Policy restricts XMLHttpRequest, but it does not limit elements with a src attribute such as <script>, <img>, and <iframe>, which can load resources from anywhere.
JSONP leverages the <script> tag’s ability to fetch and execute JavaScript from any domain. The interaction can be described as:
Front‑end page : "I need data but cannot use AJAX. I’ll create a <script> tag pointing to your URL and define a global function myCallbackFunction to receive the data."
Server : "Here’s the data wrapped in the callback you specified."
When the browser loads the injected <script>, it executes the returned JavaScript, which calls the predefined global function, delivering the data without using XMLHttpRequest and thus bypassing the same‑origin policy.
Hands‑On: Build Your Own JSONP Utility
Below is a simple, reusable jsonp function that accepts an options object containing url, params, and callback:
/**
* A simple JSONP implementation
* @param {object} options - configuration object
* @param {string} options.url - request URL
* @param {object} [options.params] - query parameters
* @param {string} [options.callbackKey='callback'] - name of the callback query parameter
* @param {function} options.callback - success callback
*/
function jsonp({ url, params = {}, callbackKey = 'callback', callback }) {
// 1. Generate a unique callback name to avoid collisions
const callbackName = `jsonp_callback_${Date.now()}_${Math.floor(Math.random()*100000)}`;
// 2. Attach the callback to the window object
window[callbackName] = function(data) {
// Invoke the user‑provided callback
callback(data);
// 3. Clean up: remove the global function
delete window[callbackName];
// Remove the injected script tag
document.head.removeChild(scriptElement);
};
// 4. Serialize parameters and append the callback name
const paramsArray = [];
for (let key in params) {
paramsArray.push(`${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`);
}
paramsArray.push(`${callbackKey}=${callbackName}`);
const paramsString = paramsArray.join('&');
const finalUrl = url.includes('?') ? `${url}&${paramsString}` : `${url}?${paramsString}`;
// 5. Create and inject the <script> tag to fire the request
const scriptElement = document.createElement('script');
scriptElement.src = finalUrl;
document.head.appendChild(scriptElement);
// (Optional) error handling could be added here
}How to Use It
Assume a third‑party weather service supports JSONP:
// Initiate a JSONP request
jsonp({
url: 'https://api.fedjavascript.com/jsonp/city',
params: { name: 'Beijing' },
// callbackKey: 'cb', // optional custom callback parameter name
callback: function(data) {
// Data received successfully
console.log('Current weather:', data);
// Expected format: { temperature: '35°C', condition: 'Sunny' }
}
});
// The browser will request:
// https://api.fedjavascript.com/jsonp/city?name=Beijing&callback=jsonp_callback_1752844122915_12345
// The server responds with JavaScript like:
// jsonp_callback_1752844122915_12345({ "temperature": "35°C", "condition": "Sunny" });When the returned script runs, the temporary global jsonp_callback_… function is invoked, the data is captured, and both the <script> tag and the global function are removed, leaving a clean state.
The End of the "Stone Age": JSONP Limitations
Only GET requests : The <script> tag’s src can only perform GET, so POST, PUT, etc., are impossible.
Security risks : You must fully trust the JSONP provider; a compromised server could return malicious code, leading to XSS attacks.
Primitive error handling : Unlike XMLHttpRequest, you cannot reliably capture HTTP status codes; you can only rely on onerror or timeouts, which give limited failure information.
Modern web development replaces JSONP with CORS, which uses standard HTTP headers to negotiate cross‑origin access, supports all HTTP methods, and provides robust error handling.
Why Review This History?
Even though we rarely write JSONP today, recreating this “ancient” tool offers deeper insights:
Understanding constraints fuels innovation : The Same‑Origin wall inspired the creative JSONP solution.
Seeing standards evolve : From JSONP to CORS, we witness how web standards mature for safety and capability.
Grasping the browser security model : Knowing JSONP clarifies the fundamentals of browser security and the elegance of CORS design.
JSONP stands as a living fossil in web history, showcasing early developers’ ingenuity and paving the way for today’s robust cross‑origin techniques.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
