1

I have the next HTML element that contain a javascript object:

<div ui-jq="easyPieChart"
     ui-options="{
                 percent: 75,
                 lineWidth: 5,
                 trackColor: '#e8eff0',
                 barColor: '#23b7e5',
                 scaleColor: false,
                 color: '#3a3f51',
                 size: 134,
                 lineCap: 'butt',
                 rotate: -90,
                 animate: 1000
                 }"
     class="easyPieChart">
</div>

How can I convert this string into array like bellow without changing HTML:

[{
  animate: 1000,
  barColor: "#23b7e5",
  color: "#3a3f51",
  lineCap: "butt",
  lineWidth: 5,
  percent: 75,
  rotate: -90,
  scaleColor: false,
  size: 134,
  trackColor: "#e8eff0"
}]

I already tried these versions:

$("[ui-jq]").each(function () {
  var self = $(this);
  // var options = eval('[' + self.attr('ui-options') + ']');
  var options = (new Function("return [" + self.attr('ui-options') + "];")());

  console.log(options);
});

But I receive an error regarding CSP:

Content Security Policy of your site blocks the use of 'eval' in JavaScript

The Content Security Policy (CSP) prevents the evaluation of arbitrary strings as JavaScript to make it more difficult for an attacker to inject unauthorized code on your site.

To solve this issue, avoid using eval(), new Function(), setTimeout([string], ...) and setInterval([string], ...) for evaluating strings.

I created a this JSFiddle to help you to show me an example faster: https://jsfiddle.net/oqwc1j58/4/

Thank you

  • 2
    Well don't change the HTML if you don't want to, but you're just making your life harder/ – Pointy Dec 27 '22 at 16:52

3 Answers3

2

If you make the attribute a data- attribute, and you make the value proper JSON, then jQuery will do that for you:

    $("[ui-jq]").each(function () {
      var self = $(this);
      var options = self.data("uiOptions");
    
      console.log(options);
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div ui-jq="easyPieChart"
     data-ui-options='{
                 "percent": 75,
                 "lineWidth": 5,
                 "trackColor": "#e8eff0",
                 "barColor": "#23b7e5",
                 "scaleColor": false,
                 "color": "#3a3f51",
                 "size": 134,
                 "lineCap": "butt",
                 "rotate": -90,
                 "animate": 1000
                 }'
       class="easyPieChart">
    </div>

When jQuery sees that a data- attribute value can be parsed as valid JSON, it does that implicitly and returns the resulting value when you load it via .data(). Note that the camel-case version of the name (I think) should be used, but jQuery might work with either version. Note also that you leave off the "data-" prefix.

Pointy
  • 405,095
  • 59
  • 585
  • 614
1

This is kind of janky but you can parse the HTML yourself and create the array you're looking for:

// From https://stackoverflow.com/a/72773057/378779
const sortObjectByKeys = (object, asc = true) => Object.fromEntries(
  Object.entries(object).sort(([k1], [k2]) => k1 < k2 ^ !asc ? -1 : 1),
)

$("[ui-jq]").each(function () {
  let options = $(this).attr('ui-options').replace('{', '').replace('}', '').split('\n')

  let o = {}
  options.filter(function(option) { return ! option.match(/^\s*$/) }).forEach(function(option){
      option = option.trim().replace(/,$/, '')
      let a = option.split(/: /)
      o[a[0]] = a[1].replace("'", '')
  })
  console.log(sortObjectByKeys(o))
});
</script>
<script src="https://code.jquery.com/jquery-3.6.3.js" integrity="sha256-nQLuAZGRRcILA+6dMBOvcRh5Pe310sBpanc6+QBmyVM=" crossorigin="anonymous"></script>

<div ui-jq="easyPieChart"
     ui-options="{
                 percent: 75,
                 lineWidth: 5,
                 trackColor: '#e8eff0',
                 barColor: '#23b7e5',
                 scaleColor: false,
                 color: '#3a3f51',
                 size: 134,
                 lineCap: 'butt',
                 rotate: -90,
                 animate: 1000
                 }"
     class="easyPieChart">
</div>
kmoser
  • 8,780
  • 3
  • 24
  • 40
1

You can use JSON.parse():

const opts=$("[ui-jq]").get().map(el=>Object.fromEntries(Object.entries(JSON.parse(el.getAttribute('ui-options').replaceAll("'",'"').replace(/(\w+)(?=\s*:)/g,'"$1"'))).sort(([a],[b])=>a.localeCompare(b))));
console.log(opts);
<script src="https://code.jquery.com/jquery-3.6.3.js" integrity="sha256-nQLuAZGRRcILA+6dMBOvcRh5Pe310sBpanc6+QBmyVM=" crossorigin="anonymous"></script>

<div ui-jq="easyPieChart"
     ui-options="{
                 percent: 75,
                 lineWidth: 5,
                 trackColor: '#e8eff0',
                 barColor: '#23b7e5',
                 scaleColor: false,
                 color: '#3a3f51',
                 size: 134,
                 lineCap: 'butt',
                 rotate: -90,
                 animate: 1000
                 }"
     class="easyPieChart">
</div>
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
  • This is a more elegant solution than mine, but still doesn't order the results by key. With a little more code you could do the required sorting as well. – kmoser Dec 27 '22 at 20:42
  • OK, I added the sorting. Did not realize that that was important here. – Carsten Massmann Dec 27 '22 at 20:56
  • @kmoser You should not rely on the order of keys in an object – Bergi Dec 27 '22 at 21:54
  • @Bergi That's a complicated issue and has [several answers depending on how you intend to use the object](https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties). – kmoser Dec 28 '22 at 00:43
  • @kmoser It's only complicated when you need to do it. The general advice that you shouldn't be doing it stands. – Bergi Dec 28 '22 at 00:46