Normally, when one want to put strings to be used in javascript, esc_js
is the right function, not esc_attr
.
The problem is that esc_js
, according to docs:
Escapes text strings for echoing in JS
(bold mine).
So, using with esc_js
you obtain a string that can be safely echoed in js, not parsed: it’s not a bug, it’s the intended behaviour.
The canonical way to pass data from PHP to js in WordPress is wp_localize_script
so you should consider to use it instead. (Note that function internally do not use any esc_*
functions, just echo the result of json_encode
).
As alternative I can suggest you to use filter_var
with sanitize filters: IMHO it is a far better way to do the task esc_*
WP functions do.
Consider that esc_*
function pass through filter hooks, so integrity of your data can be corrupted by external code.
Try:
<?php
$data_safe = filter_var( $json, FILTER_SANITIZE_SPECIAL_CHARS );
$out_safe = filter_var( $json, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
?>
<textarea data-test-json="<?php echo $data_safe; ?>"><?php echo $out_safe; ?></textarea>