0

I am experimenting with a charting package called Highcharts (some of you may be familiar with it but regardless the problem is not related to Highcharts per se). What I wanted to do was have my PHP generated HTML embed a JSON object into the DOM which would then be picked up by a static jQuery listening function. Here's what it looks like:

 // Static JS file that get's loaded with every page load and // and listens for a class with ".highchart_config". // When it finds a config class it then looks in the attribute "data-chart" // for the JSON configuration object jQuery.noConflict(); jQuery(function($) { $(document).ready(function() { $(".highchart_config").each(function(index) { var config_obj = $(this).attr('data-chart'); chart = new Highcharts.Chart( config_obj ); }); }); }); 

And then the HTML is as follows:

 <div class="highchart_config" data-chart=' {chart: {"renderTo":"chart2","defaultSeriesType":"column"},title: {"text":"Monkies are Happy Animals"},xAxis:{"categories":["Apples","Oranges","Pears","Grapes","Bananas"],"min":null,"title":""},yAxis: {"min":0,"title":{"text":"Total fruit consumption"}},legend: {"align":"center","x":0,"verticalAlign":"bottom","y":0,"floating":false,"backgroundColor":null,"borderColor":"#CCC","borderWidth":1,"shadow":false,"reversed":true},tooltip: { formatter: function() { return this.series.name + ":" + this.y + " "}},plotOptions: {"column":{"stacking":"normal","dataLabels":{"enabled":false}}},series: [{"name":"Running","data":[5,3,4,7,2]},{"name":"Cycling","data":[2,2,3,2,1]},{"name":"Lifting","data":[3,4,4,2,5]}]}'></div> 

Using a debugger I can see this working by placing a breakpoint on the line where Highcharts object instantiation takes place. When the breakpoint is hit I print the value of "chart_obj" which comes out as:

 {chart: {"renderTo":"chart2","defaultSeriesType":"column"},title: {"text":"Monkies are Happy Animals"},xAxis:{"categories":["Apples","Oranges","Pears","Grapes","Bananas"],"min":null,"title":""},yAxis: {"min":0,"title":{"text":"Total fruit consumption"}},legend: {"align":"center","x":0,"verticalAlign":"bottom","y":0,"floating":false,"backgroundColor":null,"borderColor":"#CCC","borderWidth":1,"shadow":false,"reversed":true},tooltip: { formatter: function() { return this.series.name + ":" + this.y + " "}},plotOptions: {"column":{"stacking":"normal","dataLabels":{"enabled":false}}},series: [{"name":"Running","data":[5,3,4,7,2]},{"name":"Cycling","data":[2,2,3,2,1]},{"name":"Lifting","data":[3,4,4,2,5]}]} 

That looks "right" to me but it doesn't work. Instead the instantiation of the object fails as the config_obj is somehow malformed. To make sure I wasn't making some stupid syntax error I cut and paste the value in config_obj and put it into a static JS file that looks like this:

 $(function () { var chart; $(document).ready(function() { chart = new Highcharts.Chart({ chart: {"renderTo":"chart2","defaultSeriesType":"column"},title: {"text":"Monkies are Happy Animals"},xAxis: {"categories":["Apples","Oranges","Pears","Grapes","Bananas"],"min":null,"title":""},yAxis: {"min":0,"title":{"text":"Total fruit consumption"}},legend: {"align":"center","x":0,"verticalAlign":"bottom","y":0,"floating":false,"backgroundColor":null,"borderColor":"#CCC","borderWidth":1,"shadow":false,"reversed":true},tooltip: { formatter: function() { return this.series.name + ":" + this.y + " "}},plotOptions: {"column":{"stacking":"normal","dataLabels":{"enabled":false}}},series: [{"name":"Running","data":[5,3,4,7,2]},{"name":"Cycling","data":[2,2,3,2,1]},{"name":"Lifting","data":[3,4,4,2,5]}] }); }); }); 

This "hardcoded" method works and yet the instantiation call should have precisely the same configuration object passed in. I'm at a loss now how to proceed. I have been reading other posts on stackoverflow around this topic but can't find anything to help me with my specific problem. Any and all help is greatly appreciated.

UPDATE: I have no tried ... to no avail using both data() and attr() methods and in both cases with and without a call to JSON.parse(config_obj). It DOES appear that the problem is related to config_obj being treated as a string so in the debugger I decided to assign a variable "test" to the cut-and-pasted string results of config_obj without the exterior quotation marks. It works fine so it's clearly a well structured JSON string but getting it converted to a string is still eluding me. Below I have an image of my debugging session which shows three things:

  1. First I get an error when using the JSON.parse() function on my config_obj string (that's true regardless if I used data() or attr() to retrieve config_obj from the DOM)
  2. If I instead just cut-and-paste the text into a test variable called "test" it is recognised as a valid JS object
  3. If I use the JSON.stringify() method on the test object it converts back to a string version that is CLOSE to the same as my config_obj variable ... the difference being that the first level attributes in the object have quotation marks around them. This might be a hint at what's going wrong but I still haven't cracked this nut ... any help would be greatly appreciated.

assign to variable then stringify

4
  • can you call parseJSON before set to chart? var config_obj = $.parseJSON($(this).attr('data-chart')); chart = new Highcharts.Chart( config_obj ); Commented Aug 4, 2012 at 8:19
  • chart = new Highcharts.Chart( JSON.parse(config_obj) ); Commented Aug 4, 2012 at 8:19
  • Tip: jQuery's data will attempt to automatically convert values of data-* attributes to json and other formats, attr will not. If you fetch the value using $(this).data('chart'), you should get already parsed JSON. Commented Aug 4, 2012 at 8:23
  • I have switched to using the data() method but the return value appears to be the same and as others have suggested it looks like maybe it's a string representation still. Commented Aug 5, 2012 at 14:59

2 Answers 2

1

When you get the attributes value - using .attr() - what you're being returned is a string. You'll need to parse that string to turn it into the actual object, so change the following line to:

chart = new Highcharts.Chart( JSON.parse(config_obj) ); 

It's the JSON.parse() function that's the important part.

Also, as a note, if you're using data-* attributes, it's better to use the .data() function, so you'd change the other line to:

var config_obj = $(this).data('chart'); 
Sign up to request clarification or add additional context in comments.

4 Comments

good call on the data() function, didn't realise that was available.
I have tried using the data() function and I'm using the JSON.parse (config_obj) as suggested but now what I get is a "Uncaught SyntaxError: Unexpected toeken c"
@ken Looking at the documentation for .data(), it looks like jQuery will attempt to convert to Javascript types (booleans, objects, arrays, etc) where possible, so you shouldn't need to call JSON.parse() if you use the data() function.
Yes it seems strangely persistent in NOT converting to an object. I have tried with and without the parse() call and with data() and attr(). If I go into the browsers debugger and print the value of config_obj and then assign a variable "test" to the cut-and-paste of the value of config_obj (aka, without the quote marks on the exterior) then it immediately works and the "test" object is an object and not a string.
0

As you may have seen in my "update" to the question I had found a variation in the string versions of the JSON object between my original object and the one I created by cut-and-pasting this same string into an object and then running JSON.stringify() on it.

This variation -- including double quote markers around object names -- seems to be important for it to work correctly. If you pass it in this way using jQuery's .data() method than it automatically converts it to a JSON object and there's no need to directly call JSON.parse().

I still find it odd that there is a stricter standard to convert a string to an object with JSON's parse() method than there is within JS itself and I'd be interested if anyone has any theories on this. In either event, wanted to thank @Anthony, @DCoder and everyone else who helped.

Here is the working DOM entry:

<div class="highchart_config" data-chart='{"chart":{"renderTo":"chart2","defaultSeriesType":"column"},"title":{"text":"Monkies are Happy Animals"},"xAxis":{"categories":["Apples","Oranges","Pears","Grapes","Bananas"],"min":null,"title":""},"yAxis":{"min":0,"title":{"text":"Total fruit consumption"}},"legend":{"align":"center","x":0,"verticalAlign":"bottom","y":0,"floating":false,"backgroundColor":null,"borderColor":"#CCC","borderWidth":1,"shadow":false,"reversed":true},"tooltip":{},"plotOptions":{"column":{"stacking":"normal","dataLabels":{"enabled":false}}},"series":[{"name":"Running","data":[5,3,4,7,2]},{"name":"Cycling","data":[2,2,3,2,1]},{"name":"Lifting","data":[3,4,4,2,5]}]}'></div> 

And the JS that takes this DOM entry as input is:

jQuery.noConflict(); jQuery(function($) { $(document).ready(function() { $(".highchart_config").each(function(index) { var config_obj = $(this).data('chart'); chart = new Highcharts.Chart( config_obj ); }); }); }); 

1 Comment

I feel slightly foolish for not checking your JSON in the first place; I kind of just assumed it was a valid JSON string. Keys being contained within double quotes is just one of the rules that defines the JSON syntax. They're slightly more strict than the rules for defining a regular JavaScript object in code, but I'd assume that's because JSON is intended as a language-independent way of exchanging data.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.