Почему CDATA нужен и не работает везде одинаково?

В консолях Firefox и Chrome это работает (предупреждает содержимое скрипта):

var script = document.createElement("script");
script.textContent = (
    function test() {
        var a = 1;
    }
);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

Использование этого кода в качестве скрипта Greasemonkey для Firefox также работает.

Теперь, если вы хотите добавить «частный метод» do() в test(), он больше не работает ни в консоли Firefox/Chrome, ни в скрипте Greasemonkey:

var script = document.createElement("script");
script.textContent = (
    function test() {
        var a = 1;
        var do = function () {
            var b = 2;
        };
    }
);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

Чтобы это работало в скрипте Greasemonkey, я должен поместить весь код в блок CDATA tag:

var script = document.createElement("script");
script.textContent = (<![CDATA[
    function test() {
        var a = 1;
        var do = function() {
            var b = 2;
        };
    }
]]>);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

Это работает только в сценарии Greasemonkey; он выдает ошибку из консоли Firefox/Chrome. Я не понимаю, почему я должен использовать тег CDATA, у меня нет здесь правил XML, которые нужно соблюдать, потому что я не использую XHTML.

Чтобы это работало в консоли Firefox (или Firebug), мне нужно поместить CDATA в такие теги, как <> и </>:

var script = document.createElement("script");
script.textContent = (<><![CDATA[
    function test() {
        var a = 1;
        var do = function() {
            var b = 2;
        };
    }
]]></>);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

Это не работает из консоли Chrome. Я пытался добавить .toString() в конце, как это делают многие (]]></>).toString();), но это бесполезно.

Я попытался заменить <> и </> именем тега <foo> </foo>, но это тоже не сработало.

Почему мой первый фрагмент кода не работает, если я определяю var do = function(){} внутри другой функции?

Почему я должен использовать CDATA в качестве обходного пути, даже если я не использую XHTML?

И зачем мне добавлять <> </> для консоли Firefox, если она работает без скрипта Greasemonkey?

Наконец, какое решение для Chrome и других браузеров?

ИЗМЕНИТЬ:

Плохо, я никогда не использовал do-while в JS, и я создал этот пример в простом текстовом редакторе, поэтому я не видел, что «делать» было зарезервированным ключевым словом: p

Но проблема все еще здесь, я не инициализировал класс Javascript в своих примерах. В этом новом примере CDATA требуется для Greasemonkey, Firefox требуется CDATA между E4X <> </>, а Chrome не работает:

var script = document.createElement("script");
script.textContent = (
<><![CDATA[var aClass = new AClass();
function AClass() {
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();]]></>
);
document.getElementsByTagName("head")[0].appendChild(script);

Вопрос: почему?


person baptx    schedule 22.06.2012    source источник
comment
Есть ли какая-то особая причина, по которой вы создаете элементы сценария и устанавливаете их текстовое содержимое в JavaScript? Это не имеет никакого смысла вообще. Что ты пытаешься сделать?   -  person Tomalak    schedule 22.06.2012
comment
@Tomalak Я только что закончил проект HTML5/Javascript, в нем используется класс с частными атрибутами, некоторые из них используются для конфигурации приложения, и я хочу иметь возможность изменять эти атрибуты с помощью сценария Greasemonkey без изменения конфигурации исходного файла по умолчанию. Мне нужно переопределить основную функцию Javascript stackoverflow.com/questions/4064035/   -  person baptx    schedule 22.06.2012
comment
@Tomalak Я просто делаю предупреждение в конце, чтобы вы могли скопировать / вставить код и посмотреть, есть ли ошибка или нет! ;) Обратите внимание, что атрибуты, которые я хочу изменить, используются для отладки приложения, они не важны для функциональности программы, иначе я бы изменил эти атрибуты через PHP $_GET :)   -  person baptx    schedule 22.06.2012


Ответы (2)


Здесь есть несколько ошибок/проблем:

  1. Добавление закрытого метода do() не сработало, поскольку do является зарезервированным словом!
    Этот код отлично работает как в FF, так и в Chrome (и в сценарии GM):

    var functionVar = (
        function test() {
            var a = 1;
            var properlyNamedVariable = function() {
                var b = 2;
            };
        }
    );
    console.log (functionVar.toString() );
    

    Если вы используете описательные имена переменных, вы не только избавите себя от многих проблем в будущем, вы почти никогда не будете случайно использовать зарезервированное слово.

  2. Re: "To make this work in a Greasemonkey script, I have to put all code in a CDATA tag".

    Нет, этот код не работал. Ошибка произошла в области evald в песочнице Greasemonkey, и о ней не было сообщено в консоль ошибок Firefox. То есть он молча провалился.
    Если вы попытаетесь выполнить test(); из консоли, вы получите сообщение об ошибке: ReferenceError: test is not defined. Принимая во внимание, что если вы удалите код var do = function ... и перезагрузите страницу, вы можете вызвать test() из консоли.

  3. Точно так же код script.textContent = (<><![CDATA[ ... также не работал. Он также молча провалился, и test() не был определен. Это немного другой вид скрытого сбоя, чем в предыдущем примере.

  4. Код, основанный на трюке (<><![CDATA[ ..., не работает в Chrome, потому что Chrome не поддерживает встроенную обработку XML в javascript. Это преимущество Firefox, который поддерживает E4X, а Chrome — нет.

  5. Пожалуйста, отформатируйте свой код, чтобы его не было такой рутиной при чтении.


Итак, в резюме:

  1. Используйте описательные имена для переменных, функций и т. д.
  2. Остерегайтесь зарезервированных слов в используемом вами языке.
  3. Остерегайтесь тихих неудач. К сожалению, это происходит в различных сценариях Greasemonkey.
  4. Разные браузеры поддерживают разные функции.

Вот метод, который работает в Firefox, Chrome, некоторых других браузерах, а также Greasemonkey, Tampermonkey, Scriptish и т. д. Он также помогает при отладке и тестировании, поскольку код отделен от встроенных объявлений переменных:

  1. Определите свою функцию или обычный код:

    function testMyAdhocCode () {
        "use strict";    
        var importInteger   = 1;
        var imA_PrivateFunction = function () {
            var someOtherInteger = 2;
    
            console.log (
                "importInteger = ", importInteger,
                "  ||  someOtherInteger = ", someOtherInteger
            );
        };
    
        console.log ("Greetings from testMyAdhocCode()!");
        imA_PrivateFunction ();
    }
    


  2. Затем вставьте его на страницу следующим образом (если необходимо. Лучше вообще не вводить код, если можете помочь.):

    addJS_Node (testMyAdhocCode);   //-- Function is just created.
    
    // OR
    addJS_Node (null, null, testMyAdhocCode); //-- Function is created and immediately run.
    
    // OR
    addJS_Node ("var someVar = 86;") //-- Adhoc code
    
    // OR
    addJS_Node ("preExistingFunction (42); ") //-- Run a preexisting function.
    


  3. Где addJS_Node() включен в ваш скрипт как:

    function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
        var D                                   = document;
        var scriptNode                          = D.createElement ('script');
        if (runOnLoad) {
            //--- Doesn't always fire on Chrome. Needs @run-at document-start.
            scriptNode.addEventListener ("load", runOnLoad, false);
        }
        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;
        targ.appendChild (scriptNode);
    }
    




Обновление для дополнительного вопроса:

Новая проблема больше похожа на другую.

  • Это неправильный код, поэтому он выдает ошибку в консоли Firefox.
  • Он использует функцию E4X, поэтому не будет работать в Chrome.
  • Он должен выдать ту же ошибку в Greasemonkey, что и в Firefox (номинально это один и тот же движок JS), но из-за чистой удачи в том, как GM изолирует скрипты, вам сойдет с рук это ошибочный код (пока что).
  • Не кодируйте таким образом! Используйте такую ​​функцию, как addJS_Node().

Если вам действительно нужно внедрить код, правильный способ сделать это так:

function main () {
    "use strict";   // Keep this line!
    var aClass = new aClass();

    function aClass() {
        var a = 1;
        var aPrivateMethod = function() {
            var b = 2;
            alert(b);
        };
        this.aPublicMethod = function() {
            var c = 3;
            alert(c);
        };
    }

    aClass.aPublicMethod();

    //-- PUT ALL OF THE REST OF YOUR INJECTED CODE HERE.
}

addJS_Node (null, null, main);

Плохая идея помещать все в глобальную область действия целевой страницы! Но если вы настаиваете на этом, код выглядит так:

function aClass() {
    "use strict";   // Keep this line!
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}

addJS_Node (aClass);
addJS_Node (
      'var aClass = new aClass();'
    + 'aClass.aPublicMethod();'
);
person Brock Adams    schedule 23.06.2012
comment
И что мне делать с var aClass = new aClass(); в настоящее время? Я не могу написать эту строку в testMyAdhocCode(), потому что это функция aClass(), которую нужно инициализировать. - person baptx; 23.06.2012
comment
Спасибо, но ни одно из ваших решений не дает действительно правильного результата. Для вашего дополнительного ответа: с вашим первым решением мы не можем получить доступ к общедоступной функции с помощью aClass.aPublicMethod(); как только скрипт вставлен, потому что он находится в другой функции. Ваше второе решение создает 2 тега скрипта, это не лучший способ. Я добавил ответ, он работает в разных браузерах, и нам не нужна специальная функция addJS_Node(), вы тоже используете строгую строку (я не понял, для чего она нужна), и она всегда работает без события onload на chrome. - person baptx; 24.06.2012

Все права на @BrockAdams Почему CDATA нужен и не работает везде одинаково? за идею написать все в функцию и вставить содержимое в тег script, спасибо.

Благодаря @polygenelubricants javascript get function body за получение содержимого функции main() только, а не функцию с ее содержанием.

var script = document.createElement("script");
function main()
{
var aClass = new AClass();
function AClass()
{
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();
}
var entire = main.toString();
var body = entire.substring(entire.indexOf("{") + 2, entire.lastIndexOf("}"));
script.textContent = body;
document.getElementsByTagName("head")[0].appendChild(script);

«+2» после entire.indexOf("{") нужен для того, чтобы не выбирать символ «{» и не иметь пустой новой строки в начале содержимого тега скрипта.

Функция класса Javascript не должна иметь то же имя, что и переменная, в которой мы сохраняем экземпляр класса, в Firefox aClass.aPublicMethod(); будет вызываться автоматически только при первом выполнении кода (вы не можете переопределить функцию класса), Chrome все равно обозначения, это работает.

Итак, я выбрал AClass для имени класса и aClass для объекта класса.

Любопытно, что кажется, что Firefox анализирует/отображает функцию так, как ему нравится, когда мы используем метод .toString(), например, когда скрипт вставлен, его содержимое в Firefox Inspector будет отображаться только в одной строке. Firebug отформатирует код с пробелом «табуляция» перед каждой новой строкой, даже если мы удалим пробелы при вставке функции main() (из-за того, что Firebug знает, что функция main() была перед подстрокой, поэтому он добавляет авто-пробел ). Firebug также добавляет новую строку до и после функции AClass(); когда возвращаемое значение функции сохраняется в переменной, такой как aPrivateMethod или aPublicMethod, оно отображается в 1 строке. Еще одно странное изменение Firebug заключается в том, что var aClass = new AClass(); становится var aClass = new AClass;.

function AClass()
{
    // code
}

становится

function AClass() {
    // code
}

и так далее...

Консоль Chrome всегда учитывает пробелы/новые строки, как вы написали в функции main().

Я думаю, что это то, как была создана консоль отладки, и это не имеет значения. Кстати, все эти модификации, о которых я говорил, не появляются в Firebug при использовании расширения E4X.

Забавно видеть разницу при использовании метода function.toString() вместо расширения E4X. Может у кого есть объяснение :)

Результат консоли Firefox с методом function.toString():

var aClass = new AClass; function AClass() { var a = 1; var aPrivateMethod = function () {var b = 2;alert(b);}; this.aPublicMethod = function () {var c = 34;alert(c);}; } aClass.aPublicMethod();

Результат консоли Firefox с расширением E4X:

var aClass = new AClass(); function AClass() { var a = 1; var aPrivateMethod = function () { var b = 2; alert(b); }; this.aPublicMethod = function () { var c = 34; alert(c); }; } aClass.aPublicMethod();

Результат Firebug с методом function.toString():

    var aClass = new AClass;

    function AClass() {
        var a = 1;
        var aPrivateMethod = function () {var b = 2;alert(b);};
        this.aPublicMethod = function () {var c = 3;alert(c);};
    }

    aClass.aPublicMethod();

Результат Firebug с расширением E4X и результат консоли Chrome с методом function.toString() (E4X не поддерживается) одинаковы:

var aClass = new aClass();
function aClass() {
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();

Может быть связано, E4X - это чистый способ сделать это без взлома подстроки, но поддерживается только Firefox: Создание многострочных строк в JavaScript

person Community    schedule 24.06.2012
comment
Хорошо, где-то есть проблема, мой класс Javascript просто не читается, если я переопределяю его с помощью function.toString() вместо E4X, все методы написаны в 1 строку! o_O Невозможно установить какие-либо точки останова, и Firebug даже не видит скрипт, добавленный с помощью этого метода, он отсутствует на вкладке скриптов ›‹ Я только что заметил, что при использовании E4X скрипт появляется на вкладке скриптов Firebug, но методы класса тоже пишутся в 1 строку! Вкладка HTML подходит для E4X, это проблема Firebug? - person baptx; 24.06.2012