Welcome Guest, Not a member yet? Register   Sign In
Using JSON in data attributes with JS
#1

I don't know if it's considered bad practice, or if it's the way everyone does it, but I find myself inserting JSON data into HTML element attributes all the time. So, for instance:

PHP Code:
foreach( $query->result_array() as $row )
    
$arr[] = (object) array_map'html_escape'$row );

$json json_encode$arr );

$json_string str_replace([
    
'"',
    
''',
    
'& #039;' // space here for codeigniter forum
], [
    
'\u0022',
    
'\u0027',
    
'\u0027'
], $json ); 

Now I know that json_encode has options like JSON_HEX_APOS and JSON_HEX_QUOT, but I also want to use html_escape() which is why I'm using str_replace the way I am. This allows me to do this:


PHP Code:
echo '<div data-json=\'' $json_string '\'>'

Then later in my json I can use JSON.parse() on the contents of that data-json attribute.

What are your thoughts here? Is there a better way?
Reply
#2

(This post was last modified: 11-14-2017, 05:36 PM by ivantcholakov. Edit Reason: typos )

For views I use the Twig parser and here is a test on how I would do this:

Code:
<div id="test_element" data-json="{{ '{ "value_1": "A test string value.", "value_2": 2 }'|e('html_attr') }}"></div>

<script>

    $(function() {

        var data_json = $('#test_element').attr('data-json');
        alert(data_json);

        var data = JSON.parse(data_json);
        alert(data.value_1);
        alert(data.value_2);
    });

</script>

This works, and the produced HTML code looks like this:

Code:
<div id="test_element" data-json="&#x7B;&#x20;&quot;value_1&quot;&#x3A;&#x20;&quot;A&#x20;test&#x20;string&#x20;value.&quot;,&#x20;&quot;value_2&quot;&#x3A;&#x20;2&#x20;&#x7D;"></div>

<script>

    $(function() {

        var data_json = $('#test_element').attr('data-json');
        alert(data_json);

        var data = JSON.parse(data_json);
        alert(data.value_1);
        alert(data.value_2);
    });

</script>

For PHP views this example would be:

Code:
<div id="test_element" data-json="<?php echo html_attr_escape('{ "value_1": "A test string value.", "value_2": 2 }'); ?>"></div>

<script>

    $(function() {

        var data_json = $('#test_element').attr('data-json');
        alert(data_json);

        var data = JSON.parse(data_json);
        alert(data.value_1);
        alert(data.value_2);
    });

</script>

The helper function html_attr_escape() is introduced by me, it calls the Twig's escaper to do the job.
https://github.com/ivantcholakov/starter...n.php#L283

If you don't need Twig to be installed in your project, you can implement html_attr_escape() by using the escaper form Zend. https://github.com/zendframework/zend-es...r.php#L155 The escapers of Twig seem to be ported from Zend. I can see that CodeIgniter 4 would use the Zend Escaper, you can back-port this function: https://github.com/bcit-ci/CodeIgniter4/...n.php#L219 :

Code:
<div id="test_element" data-json="<?php echo esc('{ "value_1": "A test string value.", "value_2": 2 }', 'attr'); ?>"></div>

In conclusion, you should find a way to escape the data html attribute as html attribute, it does not matter whether it would contain json data or something else. I think, the trivial way would be the best here, everyone would understand it without getting bored or tired.
Reply
#3

ivantcholakov, that Escaper class is very thorough. My only concern is that when testing, it took a 41 character string of json and turned it into 127 characters.

So this:


PHP Code:
$escaper = new Escaper('utf-8');
$str $escaper->escapeHtmlAttr(json_encode([
 
   'yodels' => 'Brian\'s',
 
   'lemons' => '"Lovely"'
]));
echo 
$str

Became this:


Code:
&#x7B;&quot;yodels&quot;&#x3A;&quot;Brian&#x27;s&quot;,&quot;lemons&quot;&#x3A;&quot;&#x5C;&quot;Lovely&#x5C;&quot;&quot;&#x7D;

If I've got a lot of json, I guess I might need to put it somewhere besides an attribute.

Thanks for the links. That Escaper class is pretty cool. Definitely useful and easier to use than rolling my own.
Reply
#4

Another possible way that I used in the past is using base_64 encoded strings, they could be put as attributes directly. But this is a complication that I don't like, the JavaScript should decode from base_64 first and then to decode the JSON string.
Reply
#5

(11-14-2017, 10:48 PM)ivantcholakov Wrote: Another possible way that I used in the past is using base_64 encoded strings, they could be put as attributes directly. But this is a complication that I don't like, the JavaScript should decode from base_64 first and then to decode the JSON string.

I haven't used that technique, but I saw that it can be done, decoding in JS with atob(). For me I just want something that is easy and foolproof.
Reply
#6

About base_64 I used this JavaScript: http://www.webtoolkit.info/javascript-ba...gxPf3RSxhG , it worked for me. Probably there are other similar ones.

I did not try atob(), I am not sure about it: https://developer.mozilla.org/en-US/docs...de_Problem
Reply
#7

(11-15-2017, 07:33 AM)ivantcholakov Wrote: About base_64 I used this JavaScript: http://www.webtoolkit.info/javascript-ba...gxPf3RSxhG , it worked for me. Probably there are other similar ones.

I did not try atob(), I am not sure about it: https://developer.mozilla.org/en-US/docs...de_Problem

 Re the second link, gotta laugh at "Solution #1 – escaping the string before encoding it". That would just make the string even longer.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB