EDIT 2 After even more research, this is actually caused by the discovery of the XSS browser as indicated by OP (unlike any WordPress-specific features). The problem only occurs when you click the Preview button in WordPress and only on this initial page load. Apparently, WordPress sends some HTML files to the request headers and runs the XSS functions in the browser. If you download the preview and then refresh the page, the XSS problem disappears and the javascript: link javascript: displayed as it is saved. When viewing the actual site after publishing the page, this problem with XSS is never encountered.
EDIT After more in-depth research (working with @gnarf ), it turns out that the actual problem comes down to WordPress handling javascript: in its preview function. It would seem that WordPress has its own Javascript, which launches and converts all javascript: links to javascript:void(0) links (removing any custom code), but only if you are viewing a page. After publishing the page, javascript: links javascript: displayed correctly.
Source mail (describes how to stop WordPress from deleting javascript: links javascript: when saving the post as a non-admin user, which I suggested that the original problem could be)
WordPress seems to be crowding out HTML in the content_save_pre filter. In particular, he calls the wp_kses_bad_protocol method in wp-includes\kses.php :
function wp_kses_bad_protocol($string, $allowed_protocols) { $string = wp_kses_no_null($string); $iterations = 0; do { $original_string = $string; $string = wp_kses_bad_protocol_once($string, $allowed_protocols); } while ( $original_string != $string && ++$iterations < 6 ); if ( $original_string != $string ) return ''; return $string; }
The $allowed_protocols is retrieved using the wp_allowed_protocols() method, which applies the kses_allowed_protocols filter to the protocol list.
With this information, you can bind the kses_allowed_protocols filter to add javascript as a valid one (note that this, of course, will open up security issues):
add_filter( 'kses_allowed_protocols', function ($protocols) { $protocols[] = 'javascript'; return $protocols; });
One way to increase the security of this approach would be to add validation for specific users or certain roles (by default it looks like this filter does not actually run on administrative accounts, so you can use javascript: links javascript: to your heart as an administrator) until the protocol is resolved javascript .