3

I am trying to implement jQCloud word cloud with click event handler. It requires me to pass a javascript function in JSON.

In C#, I have made the dynamic JSON text

foreach (var r in result)
{
    sbChart.Append("{\"text\": \"" + r.Key + "\", \"weight\": " + r.Count().ToString() + ", ");
    sbChart.Append("\"handlers\": { \"click\": \"function() { alert('You clicked " + r.Key + "');}\"}}, ");
}
if (sbChart.Length != 0)
{
    returnString = "[" + sbChart.ToString().Substring(0, sbChart.Length - 2) + "]";
}

I return this through web method to javascript where my code is

var words = JSON.parse(strJSON);
$('#div').jQCloud(words);

The JSON generated is

[
  {"text": "the", "weight": 111, "handlers": { "click": "function() { alert('You clicked the');}"}}, 
  {"text": "in", "weight": 66, "handlers": { "click": "function() { alert('You clicked in');}"}}
]

However, since my function is a string, it does not gets execute as a object. And if I remove the double quotes before and after the function statement, it gives me Invalid Character error during parse.

Please can anyone help me as to how can I make this alert work?

Hitesh
  • 3,449
  • 8
  • 39
  • 57

3 Answers3

3

If you absolutely have to pass a function to your page that way (see the "but" below), you could use eval for it, since you're the one providing the text you'll be evaling it doesn't have the security issue (and the speed issue is a non-issue and has been for years). I'm not sure what you want to do with the function, but for instance:

$(".foo").on("click", eval(words[0].handlers.click));

...would eval the function and assign the result as a click handler on the elements matching the .foo selector.

But, there's almost certainly a better way to structure things. Instead of passing back functions in the JSON, you might have those functions in your JavaScript already in some kind of map:

var clickHandlers = {
    "the": function() { alert("You clicked the"); },
    "in": function() { alert("You clicked the"); }
};

...and then just given the key ("the", "in") in your JSON.

Or, given that they're the same thing other than what was clicked, figure out what was clicked ("the" or "in") from the element that was clicked.

Or any of a dozen other things, but what they have in common is that the functions are defined in your JavaScript code, not sent back via JSON, and then you link up those functions via information in the JSON rather than actual functions in the JSON.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

Functions are not meant to be passed as JSON, and it's a pretty big security issue to execute arbitrary logic passed by a data call. That being said, use eval to solve this.

Something like

var action = eval(response[0].handlers.click); // evaluates the string as javascript, and returns the value, in this case a function.

action();
Dylan Watt
  • 3,357
  • 12
  • 16
  • If Hitesh is the one creating the JSON, what security issue do you see? How does loading a JSON file from a server you control worry you where loading a JavaScript file from a server you control does not? – T.J. Crowder Apr 18 '15 at 08:17
  • 1
    You're right that since you control the data call, it shouldn't be more vulnerable, as long as you're sure you don't evaluate user provided content (xss issues). http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil is a great little snippet on it. More philosophical than meaningful in this case. – Dylan Watt Apr 18 '15 at 08:23
1

Thanks a lot all for your suggestions above. But I ended up using the answer from how to provide a click handler in JQCloud

I modified it a bit as per my requirement and it worked

var tag_list = new Array();
    var obj = GetCustomJsonObj(strJSON);
    for (var i = 0; i < obj.length; i++) {
        tag_list.push({
            text: obj[i].text,
            weight: obj[i].weight,
            //link: obj[i].link,
            handlers: {
                click: function () {
                    var zz = obj[i];
                    return function () {
                        alert("you have clicked " + zz.text);
                    }
                }()
            }
        });
    }
Community
  • 1
  • 1
Hitesh
  • 3,449
  • 8
  • 39
  • 57
  • it's good that you figured it out yourself but your answer is totally different from the question you were asking. In this answer you are not passing the javascript function as you originally asked. So marking this as an Accepted Answer to the question above is kind of misleading. – Khurram Hassan Aug 30 '18 at 01:03