Changing Javascript on an HTML page out of my control [duplicate]

感情迁移 提交于 2019-12-17 19:04:09

问题


I'm using an HTML page out of my control. It defines a Javascript function in an inline <script> tag and calls it in <body onload="..."> :

<html>
...
<body onload="init()">
<script type="text/javascript" language="javascript">
    function init() {
        ...
    }
</script>
...

How can I change that function before it's called? I've tried to use Greasemonkey to modify the script or to insert another script just after it to override the function, but it doesn't seem to have any effect.


回答1:


Greasemonkey can now usually do this kind of thing by leveraging the beforescriptexecute event and @run-at document-start. Note that only Firefox seems to support that event and so this will not work on Chrome. See here and here for more tedious approaches.

To change that init() function before it is called, leverage the checkForBadJavascripts() function that is defined below.

You would call it like this:

//--- New "init" function to replace the bad one.
function init () {
    //... Do what you want here...
}

checkForBadJavascripts ( [
    [false, /function\s+init(/, function () {addJS_Node (init);} ]
] );

Where function\s+init( must be unique to the <script> tag you are targeting. (Note that addJS_Node() is defined below, too.)


For example, visit this page at jsBin. You will see 3 lines of text, two of them added by JS.

Now, install the following script and revisit the page. You will see that the GM-script deleted one bad <script> tag and replaced another with our "good" JS.

// ==UserScript==
// @name        _Replace evil Javascript
// @include     http://output.jsbin.com/tezoni*
// @run-at      document-start
// @grant       none
// ==/UserScript==

/****** New "init" function that we will use
    instead of the old, bad "init" function.
*/
function init () {
    var newParagraph            = document.createElement ('p');
    newParagraph.textContent    = "I was added by the new, good init() function!";
    document.body.appendChild (newParagraph);
}

/*--- Check for bad scripts to intercept and specify any actions to take.
*/
checkForBadJavascripts ( [
    [false, /old, evil init()/, function () {addJS_Node (init);} ],
    [true,  /evilExternalJS/i,  null ]
] );

function checkForBadJavascripts (controlArray) {
    /*--- Note that this is a self-initializing function.  The controlArray
        parameter is only active for the FIRST call.  After that, it is an
        event listener.

        The control array row is  defines like so:
        [bSearchSrcAttr, identifyingRegex, callbackFunction]
        Where:
            bSearchSrcAttr      True to search the SRC attribute of a script tag
                                false to search the TEXT content of a script tag.
            identifyingRegex    A valid regular expression that should be unique
                                to that particular script tag.
            callbackFunction    An optional function to execute when the script is
                                found.  Use null if not needed.
    */
    if ( ! controlArray.length) return null;

    checkForBadJavascripts      = function (zEvent) {

        for (var J = controlArray.length - 1;  J >= 0;  --J) {
            var bSearchSrcAttr      = controlArray[J][0];
            var identifyingRegex    = controlArray[J][1];

            if (bSearchSrcAttr) {
                if (identifyingRegex.test (zEvent.target.src) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
            else {
                if (identifyingRegex.test (zEvent.target.textContent) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
        }

        function stopBadJavascript (controlIndex) {
            zEvent.stopPropagation ();
            zEvent.preventDefault ();

            var callbackFunction    = controlArray[J][2];
            if (typeof callbackFunction == "function")
                callbackFunction ();

            //--- Remove the node just to clear clutter from Firebug inspection.
            zEvent.target.parentNode.removeChild (zEvent.target);

            //--- Script is intercepted, remove it from the list.
            controlArray.splice (J, 1);
            if ( ! controlArray.length) {
                //--- All done, remove the listener.
                window.removeEventListener (
                    'beforescriptexecute', checkForBadJavascripts, true
                );
            }
        }
    }

    /*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
        See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
        Note that it does not work on acripts that are dynamically created.
    */
    window.addEventListener ('beforescriptexecute', checkForBadJavascripts, true);

    return checkForBadJavascripts;
}

function addJS_Node (text, s_URL, funcToRun) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    //--- Don't error check here. if DOM not available, should throw error.
    targ.appendChild (scriptNode);
}



回答2:


The following Greasemonkey userscript (based on this source) finally worked for me. It overrides the existing function by defining another one with the same name in a new script tag just after the existing script tag. No @run-at or beforescriptexecute was required.

var firstScript = document.body.getElementsByTagName('script')[0];
var newScript = document.createElement('script');
var scriptArray = new Array();
scriptArray.push('function init() {');
scriptArray.push('    ...');
scriptArray.push('}');
newScript.innerHTML = scriptArray.join('\n');
scriptArray.length = 0; // free this memory
firstScript.parentNode.insertBefore(newScript, firstScript.nextSibling);

I didn't have much previous experience with Greasemonkey or even Javascript, so I found the Firefox Web Developer tools indispensable, specifically:

  • the Error Console to catch the many small errors that you're bound to make. and
  • the Inspect tool to see the resulting HTML (because the regular View Source doesn't show that!).


来源:https://stackoverflow.com/questions/10472569/changing-javascript-on-an-html-page-out-of-my-control

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!