Weird characters transmitted to and from server through jQuery AJAX call

A simplified example of my app:
I have a HTML form as the output of a PHP script that gets a text from a database and fills an input of that form with it. There I can edit the text that on form submit is sent to a PHP script via a jQuery AJAX call. Through PHP the text is saved in the database and then the saved value is retrieved in PHP and sent in the JSON result of the AJAX call.
The character encoding of the HTML page is ISO-8859-1:

Let’s say the he HTML form looks like this:

<form id="my_form">
<input type="text" id="txtId" name="txt" value="" />
<input type="submit" name="btn" value="save">
</form>

On form submit this AJAX call is made:

$.ajax({
	type: "POST",
	url: "my_script.php",
	data: $("#my_form").serialize(),
	success: function (jsonObj) {
		if(!jsonObj) {
			return;
		}
		if("txt" in jsonObj) {
			$("#txtId").val(jsonObj[txt]);
		}
		return false;
	},
	error: showError,
	dataType: "json"
});

In PHP, after saving the text in the database and retrieving the saved text, I add it to an associative array which I convert into a JSON object displayed as the response of the AJAX call:

$item['txt'] = $value; //$value is the text saved in the database
header("Content-type: application/json");
echo json_encode($item);

At first I submited the form with exactly the text that came from PHP at page load. It looked fine, bun when reloading the page, a weird text filled my form input. I had the text a×b and now I got a×b.
I submitted the form again with text a×b and I studied the AJAX call response with Firebug. In both Console and Net tabs of Firebug, under Post tab of the call everything looked fine, but under Response tab I got “txt”:”a\u00d7b” instead of “txt”:”a×b”.
It looked like the text somwhere on the way back to the form got encoded in a weird manner. The × character is a Windows-1252 encoded character, not an UTF-8 encoded character and I should discover where the encoding of the text changed.
I submitted the correct text again and outputted the text saved in the database without json_encode-ing it:

echo $item['txt'];

In Firebug, in the Response tab of the AJAX call, even though under the Console tab of Firebug the text looked fine, under the Net tab it appeared like this: a×b.

Again I submitted the correct text and outputted the value that came via AJAX:

echo $_POST['txt'];

And again I obtained the correct text under the Console tab and the incorrect one under the Net tab in Firebug, which meant that the encoding broke before the text got to the server.

Then, under Headers tab of the call, I noticed among the Request Headers: Content-Type application/x-www-form-urlencoded; charset=UTF-8 and I thought maybe setting the character encoding of the jQuery.ajax call to ISO-8859-1 instead of UTF-8 would solve my problem:

$.ajax({
	type: "POST",
	url: "my_script.php",
	data: $("#my_form").serialize(),
	contentType: "application/x-www-form-urlencoded;charset=ISO-8859-1",
	success: function (jsonObj) {
		if(!jsonObj) {
			return;
		}
		if("txt" in jsonObj) {
			$("#txtId").val(jsonObj[txt]);
		}
		return false;
	},
	error: showError,
	dataType: "json"
});

But the result remained the same and even more, the Content-Type header did not change either. After lots of thinking and testing, I came to these conclusions:
1. If the data parameter of the jQuery.ajax call is not empty and the type parameter is set to “POST”, the character encoding of the request remains UTF-8 no matter what, so (if I want my encoding to take effect) what I would normaly put in the data parameter I should add to the query string of the url of the AJAX call and not specify or leave the data parameter empty (setting the value of empty string to the data parameter).
2. Explicitly setting the character encoding of the AJAX request to ISO-8859-1 didn’t help at all with my problem.
3. jQuery serialize function `messes up` special characters that are not UTF-8 encoded, because it uses JavaScript function encodeURIComponent which UTF-8-encodes special characters, so make sure to UTF-8-decode the texts in the server script when using jQuery serialize or JavaScript encodeURIComponent function in an AJAX call.

So I left the JavaScript code as it initially was (without specifying the contentType parameter to the jQuery.ajax call) and, in the PHP code, decoded the string before saving it in the database:

$txt = utf8_decode($_POST['txt']);

By now I have the correct text saved in the database, but another problem arises: the text in the response of the AJAX call is null. But why?
PHP function json_encode only works with UTF-8 encoded characters, that’s why. So I should have may own JSON-maker function:

function make_json($item) {
	foreach($item as $key => $value) {
		if(is_array($value)) {
			$arr[] = '"'.$key.'":'.make_json($item[$key]);
		} else {
			$arr[] = '"'.$key.'":"'.str_replace(array("\\", "\""), array("\\\\", "\\\""), $value).'"';
		}
	}
	return '{'.implode(",",$arr)."}";
}

And the code in the PHP script becomes:

header("Content-type: application/json");
echo make_json($item);

But now I get this weird result: a�b (diamond shaped character with question mark inside instead of special characters). This time in the Net tab of Firebug everything looks fine, while the diamond shaped characters appear in the Console tab and in the HTML page.

I solved it by explicitly setting (in PHP) the character encoding of the response of the AJAX call to ISO-8859-1 (thanks to this post):

header("Content-type: application/json; charset=ISO-8859-1");
echo make_json($item);

Null value of a string variable in the JSON response object of an AJAX call

I have a jQuery AJAX call for a PHP script that retrieves a text value from a database and outputs a JSON object containing it:

$.ajax({
	type: "GET",
	url: "my_script.php",
	success: function (jsonObj) {
		if("txt" in jsonObj) {
			console.log(jsonObj.txt);
		}
	},
	error: function(jXHR, textStatus, errorThrown) {		
		console.log("The info cannot be loaded.", jXHR, textStatus, errorThrown);
	},
	dataType: "json"
});

In PHP, after retrieving the text from the database, I add it to an associative array which I convert into a JSON object displayed as the response of the AJAX call:

$item['txt'] = $value; //$value is the text got from the database
header("Content-type: application/json");
echo json_encode($item);

Even though $value is not null, in the response of the AJAX call txt variable has the null value. Which means that json_encode function fails in the case of my text. As I later discovered, that is because the text I got from the database contained non-UTF-8 encoded characters and in the first parameter of the json_encode function, as PHP manual says, `All string data must be UTF-8 encoded.`
So if you want to handle non-UTF-8 encoded characters through jQuery AJAX calls with JSON data type response, you should make your own JSON encoding function. A basic example would be:

function make_json($item) {
	foreach($item as $key => $value) {
		if(is_array($value)) {
			$arr[] = '"'.$key.'":'.make_json($item[$key]);
		} else {
			$arr[] = '"'.$key.'":"'.str_replace(array("\\", "\""), array("\\\\", "\\\""), $value).'"';
		}
	}
	return '{'.implode(",",$arr)."}";
}

And the code in the PHP script goes like:

header("Content-type: application/json; charset=ISO-8859-1");
echo make_json($item);

Delayed javascript function/handler gets the right variable/parameter values

So we have a function that adds a handler function that is called after an ajax load or at a later time and we need to execute the handler function code for each item inside an array allowing the function to access the array values. Usually with the simplest try you would get some code that get’s executed with invalid variable values. Examples are below.

The wrong code:

//the function that will do the stuff for each array
var dostuff = function (arr) {
var n = arr.length,
    i;
    for(i = 0; i < n; i++) {
        var o = arr[i];
        //event bind or delayed function call
        setTimeout(function () {
                //we want to access here the correct arr[i]
                //when the code gets here the value of o is arr[arr.length-1]
                console.log(o);
        }, 1000);
    }
}
dostuff([1,2,3,4]);

You can load the code into your javascript console to see it in action.
The output appears after 1 second and it is like this:

4
4
4
4

I don't need that. I need something that works so we need to wrap the handler call into a function call that will pass the current values as parameters.

var dostuff = function (arr) {
var n = arr.length,
    i;
    for(i = 0; i < n; i++) {
        var o = arr[i];
        (function (y) {
            setTimeout(function () {
                console.log(y);
            }, 1000);
        }) (o);
    }
}
dostuff([1,2,3,4]);

The output appears after 1 second and it is like this:

1
2
3
4

The final best solution would be to have the function inside for already defined so you don't define it on every loop:

var dostuff_item = function (y) {
            setTimeout(function () {
                console.log(y);
         }, 1000);
 }
var dostuff = function (arr) {
var n = arr.length,
    i;
    for(i = 0; i < n; i++) {
        var o = arr[i];
        dostuff_item (o);
    }
}
dostuff([1,2,3,4]);

Tada!

base html tag does not apply correctly to all javascript and style files

Problem: The

<base href="http://example.com/path/to/res/">

  did not work at all on Internet Explorer – no javascript or style files were loaded and worked partially in Mozilla Firefox – a few javascript files were not pointing to the path that should be transformed by base href attribute.

Example:

Array(
[0] => 1
)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<base href="http://example.com/path/to/res/">
<link rel="stylesheet" href="./css/styles.css" type="text/css" />
<scrupt src="./js/jquery.js" type="text/javascript"></script>
<scrupt src="./js/j1.js" type="text/javascript"></script>
<scrupt src="./js/j2.js" type="text/javascript"></script>

This code produced a page with no style files and  no javascript files included in Interenet Explorer 8 and a few javascript files (j1.js and j2.js) were not included in Firefox 3.5 because base tag was not applied. After several tests and all kind of different arrangements we discovered that the problem was the output before the page source the

Array ...

thing.

Solution: no output before the page’s headers otherwise you can get all kind of strange results including a html base tag not working correctly.

Object doesn’t support property or method. Adding options in a <select> from a child window on Internet Explorer

Full error message:  Object doesn’t support property or method. I got this error message in Internet Explorer 7 every time i tried to run code similar to the next snippet:

window.opener.form.select[select.length]  = new Option(text, value);

The problem is that on Internet Explorer due to some mysterious security restrictions you are not allowed to add new options in a select element that resides in the parent (opener) window. In order to do this you need a workaround.

The easiest workaround is to write a function in the parent window that adds elements in the select element. The function should look something like this:

function addOptionToSelect(formName, selectName, optionText, optionValue) {
var elem = document.forms[formName].elements[selectName];
elem.options[elem.length] = new Option(optionText, optionValue);
}

This function has to be written in the parent window’s javascript and it will be accessed from child window with some code like this:

window.opener.addOptionToSelect(formName, selectName, optionText, optioValue);

Conclusion: Internet Explorer sucks.

jquery pngFix fixed

I tried to use the pngFix from this website: http://jquery.andreaseberhard.de/pngFix/. But it didn’t work and i received an javascript error in IE6.

After several minutes of debugging and tries i found out that the error is generated by old syntax in selecting elements. It used

jQuery(this).find("img[@src$=.png]").each(...);

instead of

jQuery(this).find("img[src$=.png]").each(...);

So the fix is: Remove the “@” character form the selector string.

The same thing for the “pack” version except that the replaced string is:

[@m$=.M]

and the replacement string is:

[m$=.M]

.

How to check/uncheck a bunch of checkboxes without using ids for the checkbox inputs

My solution of checking/unchecking a group of HTML checkboxes using Javascript implies using an array of checkboxes, which means naming all the inputs of type ‘checkbox’ like array_name[]. Example:

<form name="cb_form">
<input type="checkbox" name="cb[]" value="0" />Zero
<input type="checkbox" name="cb[]" value="1" />One
<input type="checkbox" name="cb[]" value="2" />Two
<input type="checkbox" name="cb[]" value="3" />Three
</form>

In this example, the name of the checkboxes array is cb.

Next, we place two links for checking/unchecking all checkboxes in our array. If someone clicks one of these links, the checkAll() JavaScript function is called:

<a href="" onclick="checkAll('cb_form', 'cb[]', true); return false;">Check all<a>
<a href="" onclick="checkAll('cb_form', 'cb[]', false); return false;">Uncheck all<a>

The checking/unchecking all checkboxes function in JavaScipt looks like this:

function checkAll(form_name, cb_name, value)
{
var cb_arr = document.forms[form_name].elements[cb_name];
// if the checkboxes exist
if(cb_arr)
{
// if the number of checkboxes is at least 2
if(cb_arr.length > 1)
{
// for each checkbox
for(i = 0; i < cb_arr.length; i++)
{
// check (value == true) or uncheck (value == false) it
cb_arr[i].checked = value;
}
}
else // cb_arr.length is undefined which means there is a single checkbox element that is not considered an array of one element
{
cb_arr.checked = value;
}
}
}

Note that if we only have one checkbox, the variable cb it is not considered an array, but a normal variable. This is useful in the situation of dinamically generated HTML pages (using PHP, for example) and the number of checkboxes varies from page to page.

You can test the example here:

Zero One Two Three

Check all Uncheck all