instanceof возвращает false

231
20 марта 2017, 10:02

В консоли:

тот же параграф внутри функции:

Пояснения: этот параграф находится внутри iframe, помещается он туда путем innerHTML из textarea. Тот же код но при обычной разметке отрабатывает нормально, т.е. instanceof возвращает true, хотя этот же параграф вставленный из текстового поля на первом скрине.... мистика...

Исходя из второго скриншота видно, что в массиве лежит один элемент, его конструктор HTMLParagraphElement, значит он наследовался от HTMLElement, тот в свою очередь от Element и Node. Но проверка на instanceof возвращает false, почему так происходит?

Полностью воспроизвести код здесь не удается, поэтому привожу отдельно листинг js файла.

(function () {
    /**
     * Конструктор объекта консоли ифрэйма
     * 
     * @param {any} doc 
     * @returns объект консоли
     */
    function FrameConsole(doc) {
        var console = Object.assign({}, window.console);
        var consoleStyle = doc.createElement('style');
        consoleStyle.textContent = ''; // вставить стили для консоли
        doc.head.appendChild(consoleStyle);
        /**
         * @param {any} e  new Date
         * @returns возвращает дату в виде строки "часы:минуты:секунды.миллисекунды"
         */
        function getDate(date) {
            date = new Date(date.valueOf() - 6e4 * date.getTimezoneOffset());
            var dateString = date.toISOString().replace("Z", "");
            return dateString.substring(dateString.indexOf("T") + 1);
        }
        /**
         * Преобразует входящие данные к строке
         * 
         * @param {any} data - входящие данные
         * @returns итоговый результат
         */
        // добавить вывод тегов
        function stringConvert(data) {
            var b = data[0].constructor;
            var c = data[0] instanceof HTMLElement; //!!!! здесь хром возвращает false, не пробовал оперу и ИЕ, в мозиле работает.
            data.forEach(function(item, i, arr){
                if(item instanceof HTMLElement) {
                    alert(true );
                } else {
                     alert(false);
                }
                switch (Object.prototype.toString.call(item)) {
                    case "[object Array]":
                    case "[object Object]":
                        arr[i] = JSON.stringify(item);
                        break;
                    default:
                        break;
                }
            });
            return data;
        }
        /**
         * генерирует строки в консоле
         * 
         * @param {any} data arguments метода console
         */
        function genericRow(data) {
            var eventContainer = doc.body.querySelector('.console');
            var eventDate = doc.createElement('span');
            var eventData = doc.createElement('p');
            var eventCode = doc.createElement('code');
            eventDate.className = 'console_date';
            eventData.className  = 'console_text';
            eventCode.className  = 'console_code';
            eventCode.textContent = stringConvert(data);
            eventDate.textContent = getDate(new Date());
            eventData.appendChild(eventCode);
            eventData.appendChild(eventDate);
            eventContainer.appendChild(eventData);
        }
        console.log = function () {
            var data = Array.prototype.slice.apply(arguments);
            genericRow(data);
            window.console.log.apply(null, arguments);
        }
        return console;
    }

    document.addEventListener('DOMContentLoaded', function () {
        var engine = new CodeEngine();
        engine.init();
        engine.DOM.frame.contentWindow.console = new FrameConsole(engine.DOM.frame.contentWindow.document);
        var button = document.getElementById('run');
        button.addEventListener('click', function () {
            engine.execute();
        });
    });
})();
Answer 1

Интересный вопрос, в общем суть в том, что HTMLElement в текущем scope действительно, не тот же самый инстасом которого, является параграф, просто можете проверить, начиная в вашем console.log враппере

data[0].__ptoto__  === HTMLParagraphElement //false

чтобы "исправить", нужно прокинуть ещё и window в ваш "конструктор" консоли, примерно так получилось (смотрите по коду коменты // ТУТ)

(function () {
    /**
     * текстовые поля
     * 
     * @param {any} CSSClass 
     */
    function Field(CSSClass) {
        this.elem = document.querySelector(CSSClass);
    }
    Field.prototype.write = function() {
    }
    /**
     * кнопки
     * 
     * @param {any} CSSClass 
     */
    function button(CSSClass) {
        this.elem = document.querySelector(CSSClass);
    }

    /**
     * Класс обработчика и загрузчика кода в iframe  и ткстовые поля докуметта
     * Содержит ключи (объекты) со ссылками на DOM узлы основного документа и iframe
     * 
     */
    function CodeEngine() {
        this.DOM = {
            //cssText: document.querySelector('head style'),
            htmlText: document.querySelector('.html-container'),
            codeText: document.querySelector('body script'),
            frame: document.querySelector('.code_result iframe'),
            html: document.querySelector('.code_html textarea'),
            css: document.querySelector('.code_css textarea'),
            js: document.querySelector('.code_js textarea'),
        }
    }
    /**
     * 
     * 
     */
    CodeEngine.prototype.init = function () {
        /**
         * Adds the necessary DOM elements to the iframe
         * 
         * @returns  Item object in a iframe
         */
        function renderFrame() {
            var frame = this.DOM.frame.contentWindow.document;
            var docStyle = frame.createElement('style');
            var docHTML = frame.createElement('div');
            docHTML.setAttribute('id', 'contentHTML');
            var docScript = frame.createElement('script');
            return {style: docStyle, html: docHTML, script: docScript}
        }
        /**
         * Adds to the initial data in textarea
         * 
         */
        function renderFields() {
            this.DOM.js.value = this.DOM.codeText.innerHTML;
            this.DOM.html.value = this.DOM.htmlText.innerHTML;
            //this.DOM.css.value = this.DOM.cssText.textContent;
        }
        /**
         * Running code in the iframe
         * 
         */
        function execute() {
            var frame = this.DOM.frame.contentWindow.document;
            frame.body.innerHTML = '';
            this.frameDOM.style.textContent = this.DOM.css.value;
            this.frameDOM.html.innerHTML = this.DOM.html.value;
            //this.frameDOM.script.textContent = this.DOM.js.value;
            frame.head.appendChild(this.frameDOM.style);
            frame.body.appendChild(this.frameDOM.html);
            var eventContainer =  frame.createElement('div');
            eventContainer.className = 'console';
            frame.body.appendChild(eventContainer);
            frame.body.appendChild(this.frameDOM.script);
            var newScript = frame.createElement('script');
            newScript.textContent = this.DOM.js.value;
            frame.body.replaceChild(newScript, this.frameDOM.script);
            this.frameDOM.script = newScript;

        }
        renderFields.call(this);
        this.frameDOM = this.frameDOM ? this.frameDOM : renderFrame.call(this);
        this.execute = execute.bind(this);
        this.execute();
    }
    /**
     * Конструктор объекта консоли ифрэйма
     * 
     * @param {any} doc 
     * @returns объект консоли
     */
    function FrameConsole(doc, win) { //ТУТ
        var win = win;
        var console = Object.assign({}, window.console);
        var consoleStyle = doc.createElement('style');
        consoleStyle.textContent = ''; // вставить стили для консоли
        doc.head.appendChild(consoleStyle);
        /**
         * @param {any} e  new Date
         * @returns возвращает дату в виде строки "часы:минуты:секунды.миллисекунды"
         */
        function getDate(date) {
            date = new Date(date.valueOf() - 6e4 * date.getTimezoneOffset());
            var dateString = date.toISOString().replace("Z", "");
            return dateString.substring(dateString.indexOf("T") + 1);
        }
        /**
         * Преобразует входящие данные к строке
         * 
         * @param {any} data - входящие данные
         * @returns итоговый результат
         */
        // добавить вывод тегов
        function stringConvert(data) {
            var b = data[0].constructor;
            var c = data[0] instanceof HTMLElement;  //!!!!!!!! FALSE
            data.forEach(function(item, i, arr){
              //ТУТ
                if(item instanceof win.HTMLElement) { // true !!! :) //ТУТ
                    alert(true );
                } else {
                     alert(false);
                }
                switch (Object.prototype.toString.call(item)) {
                    case "[object Array]":
                    case "[object Object]":
                        arr[i] = JSON.stringify(item);
                        break;
                    default:
                        break;
                }
            });
            return data;
        }
        /**
         * генерирует строки в консоле
         * 
         * @param {any} data arguments метода console
         */
        function genericRow(data) {
            var eventContainer = doc.body.querySelector('.console');
            var eventDate = doc.createElement('span');
            var eventData = doc.createElement('p');
            var eventCode = doc.createElement('code');
            eventDate.className = 'console_date';
            eventData.className  = 'console_text';
            eventCode.className  = 'console_code';
            eventCode.textContent = stringConvert(data);
            eventDate.textContent = getDate(new Date());
            eventData.appendChild(eventCode);
            eventData.appendChild(eventDate);
            eventContainer.appendChild(eventData);
        }
        console.log = function () {
            var data = Array.prototype.slice.apply(arguments);

            // ТУТ
            console.debug(data[0], data[0] instanceof HTMLElement, data[0] instanceof win.HTMLElement); // эта строчка поясняет ситуацию
            genericRow(data);
            window.console.log.apply(null, arguments);
        }
        return console;
    }

    document.addEventListener('DOMContentLoaded', function () {
        var engine = new CodeEngine();
        engine.init();
        engine.DOM.frame.contentWindow.console = new FrameConsole(engine.DOM.frame.contentWindow.document, engine.DOM.frame.contentWindow); //ТУТ
        var button = document.getElementById('run');
        button.addEventListener('click', function () {
            engine.execute();
        });
    });
})();

https://plnkr.co/edit/W3zqz8oLJxscbTIwgvNn?p=preview

вкратце это выглядит так:

html
<iframe id="test"></iframe>
js
var frameWindow = document.getElementById('test').contentWindow;
var frameDocument = frameWindow.document;
frameDocument.body.innerHTML='<p>test</p>';
var p = frameDocument.querySelector('p');
console.log(p instanceof HTMLElement, p instanceof frameWindow.HTMLElement);  // false, true

https://plnkr.co/edit/b21nNfj3R92lqYW9Zxkb?p=preview

в firefox это баг, т.к.

var frameWindow = document.getElementById('test').contentWindow;
var frameDocument = frameWindow.document;
frameDocument.body.innerHTML='<p>test</p>';
var p = frameDocument.querySelector('p');
HTMLElement.prototype.myTest = () => 'hello world';
try {
    console.log(p instanceof HTMLElement, p instanceof frameWindow.HTMLElement); // true, true
    console.log('LOL', document.getElementById('test') instanceof frameWindow.HTMLElement); // true
    console.log(document.getElementById('test').myTest()); // 'hello world'
    console.log(p.myTest()); // exception
} catch(e) {
  console.error(e); // myTest() is undefined...
}

ps: чтобы так не мучаться - передавайте эти данные сообщениями, библиотечку положите непосредственно во фрейм исполнения, тогда можно даже в sandboxed iframe делать (iframe src=dataURL)

READ ALSO
I need to access a variable from a .getCurrentPosition() function [требует правки]

I need to access a variable from a .getCurrentPosition() function [требует правки]

Here is the code and i want for example to access the latitudw variable outside of this function and for example set to its value

266
Перерисовать jquery ui range slider

Перерисовать jquery ui range slider

Как динамически изменить min и max в jquery ui ползунке? Мне нужно будет перерисовать элемент

314