Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

addeventlistener drop error "Uncaught TypeError: Illegal invocation"

I create new Object by function, and i create sortable method to use, but that have an error on callback function

;"use strict";
(function(){
    function libJS(){};

    libJS.prototype = {
        loopElement : function(element, options, callback){
            var libObj = this;
            var goesCallback = function(element, options, callback){
                if (!!callback && libObj.isFunction(callback)) callback(element, options);
            };

            if (libObj.isElement(element)) goesCallback(element, options, callback);
            else if (libObj.isString(element) && /^(\.|\#)[\w\d\-_]+$/g.test(element)){
                if (/^\./g.test(element)){
                    element = document.getElementsByClassName(element.replace('.', ''));
                    var length = element.length || 0, i;
                    for(i = 0; i < length; i++) goesCallback(element[i], options, callback);
                }else{
                    element = document.getElementById(element.replace('#', ''));
                    if (!!element) goesCallback(element, options, callback);
                }
            }
        },

        isElement : function(element){
            // code check
            var f=(typeof HTMLElement === 'object' || false);
                f=(f && element instanceof HTMLElement);
                f=(f||(typeof element === 'object' && element.nodeType===1 && typeof element.nodeName === 'string'));
		    return f;
        },

        isString : function(str){
            // code check
            return true;
        },

        isObject : function(obj){
            // code check
            return true;
        },

        isFunction : function(func){
            // code check
            return true;
        },

        token : function(length){
            // create token
            return 'random_string';
        },

        sortable : function(options){
            if ('draggable' in document.createElement('span')){
                var libObj = this;
                if (libObj.isObject(options)){
                    libObj.loopElement(options.element, options, function(element, options){
                        element.style.position = 'relative';
                        var childNodes = element.childNodes,
                            length = childNodes.length || 0, x;
                        for (x = 0; x < length; x++){
                            var item = childNodes[x];
                            if (item.nodeName !== '#text'){
                                item.id = 'libJS-' + libObj.token(12);
                                item.draggable = true;
                                item.style.cursor = 'pointer';
                                item.style.transition = 'all 0.5s ease';
                                item.addEventListener('dragstart', function(event){
                                    event.preventDefault();
                                    // some code
                                });
                            }
                        }
                        element.addEventListener('dragover', function(event){
                            event.preventDefault();
                        });
                        element.addEventListener('drop', function(event){
                            event.preventDefault();
                        });
                        console.log(element.__proto__.ondrop); // View Error
                    });
                }
            }else throw 'ERROR: libJS.sortable(): this browser not support drag and drop event!';
        }
    };
    window.libJs = new libJS();
})();

libJs.sortable({
   element : '.sorter'
});
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Test</title>
        <style type="text/css">
            *{
                box-sizing: border-box;
            }
            .sorter{
                border: 1px solid green;
                padding: 10px;
            }
            .sorter:after{
                display: block;
                content: '';
                clear: both;
            }
            .sorterItem{
                display: inline-block;
                float: left;
                width: 70px;
                height: 70px;
                margin-right: 10px;
                margin-bottom: 10px;
            }
            .sorterItem img{
                display: block;
                max-width: 100%;
                max-height: 100%;
                min-width:100px;
                min-height:100px;
            }
        </style>
    </head>
    <body>
        <div class="sorter">
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
            <div class="sorterItem">
                <img src="/data/upload/noimage.jpg" />
            </div>
        </div>
    </body>
</html>

And this is an Error: Uncaught TypeError: Illegal invocation

That was in goesCallback function when I drag .sorterItem and i drop it. It can kill the browser.

So, in sortable method i have console.log(element.__proto__.ondrop) to view Error at some line.

How to fix this error?

thank everyone.

like image 361
Mr Ken Avatar asked Jun 05 '17 05:06

Mr Ken


2 Answers

You cannot do console.log(element.__proto__.ondrop) as ondrop is an accessor-property.

Accessor Properties? With reference to this SO Answer

An accessor property is one that is defined in terms of getters and setters, not as a stored value that might be written to. The "pair of accessor functions" denotes the getter and the setter function.

Also see this answer

So basically by calling console.log(element.__proto__.ondrop) you are invoking the element's ondrop getter function without proper this context which results in Illegal Invocation error.

Code Example
A javascript code example to illustrate the point

var Person = {
  firstname: "John",
  lastname: "Doe",
  get fullname() {
    return this.firstname + " " + this.lastname
  },
  set fullname(name) {
    this.firstname = name.split(' ')[0]
    this.lastname = name.split(' ')[1]
  }
}

console.log(Person.fullname) // will trigger the getter function 'fullname'

Person.fullname = "Jane Doe" // will trigger the setter function 'fullname'

console.log(Person.fullname) // logs Jane Doe

So when you call console.log(element.__proto__.ondrop) you are essentially triggering the ondrop getter without a valid context.

Update:

I guess what you wanted to do was to inspect why the Drag-events were not firing and you ended up putting a console.log(element.__proto__.ondrop) which, as it's been already answered, caused the IllegalInvocation error, resulting in a totally different class of error then the one you were trying to debug!

The reason why your Drag events are not firing is the function call inside the handlers Event.preventDefault(), quoting from MDN

The Event interface's preventDefault() method tells the user agent that if the event goes unhandled, its default action should not be taken as it normally would be.

In your case, the default action means the Drag-related functionality, which, you are (inadvertently) preventing from execution!

I suggest you read more about HTML5 Drag and Drop API on MDN.

like image 125
riyaz-ali Avatar answered Nov 08 '22 22:11

riyaz-ali


The error "Illegal Invocation" means you are trying to call a callable function with the wrong context, the context being the object the method is called on. Let me break down the console.log:

console.log(element.__proto__.ondrop)

Here, you're trying to log element.__proto__.ondrop. First, let me explain element.__proto__. The accessor property on every object, Object.prototype.__proto__ is an accessor property that points to the prototype of that object and is used to resolve methods in the lookup chain. For example:

function Person(name) { //This is a constructor function
  this.name = name; //Set the name to be part of the instance
} 
Person.prototype.sayName = function() { //A function on the prototype
  console.log(this.name);               //and is part of the instance
}

const bob = new Person("Bob");
console.log(bob.__proto__); //The __proto__ property refers to `Person.prototype`
                            //and will log the `Person.prototype` object

Here, __proto__ of an instance refers to the prototype of the constructor that constructed the instance. Now, in your case, the __proto__ of an element from the DOM would be something along the lines of the prototype of HTMLDivElement or something else depending on what type of element it is. For the purpose of this answer, we'll say it's a div.

Now, when you try to access the property ondrop of HTMLDivElement's prototype, you get an illegal invocation because you're trying to access ondrop statically from the HTMLDivElement prototype, not an actual div element! This leads to the "wrong context" because you're trying to call the accessor on HTMLDivElement.prototype, not a div from the DOM!

To "fix this", don't access the __proto__ key. If you want to access the element's ondrop property, just do element.ondrop.

like image 45
Andrew Li Avatar answered Nov 08 '22 22:11

Andrew Li