Prevent React dev tools from changing props/state - javascript

Is there a way to prevent any prop/state change from front-end on production?
I tried following but it completely disables the dev tools:
if (typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__ === 'object') {
for (let [ key, value ] of Object.entries(
window.__REACT_DEVTOOLS_GLOBAL_HOOK__
)) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__[key] =
typeof value === 'function' ? () => {} : null;
}
}

I use the following bit of code in my Meteor application that uses React 16.3 as the UI library.
The window.__ALLOW_REACT_DEVTOOLS__ is just a flag I set in the SSR html sent from the server because this line of code needs to preclude any React code, and I need it before process.env is available in the browser. On the server I set that value to false in production.
<script>
if (
!window.__ALLOW_REACT_DEVTOOLS__ &&
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ &&
typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__ === "object"
) {
for (let [key, value] of Object.entries(window.__REACT_DEVTOOLS_GLOBAL_HOOK__)) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__[key] = typeof value == "function" ? ()=>{} : null;
}
}
delete window.__ALLOW_REACT_DEVTOOLS__;
</script>
The key to making this work is that it is loaded BEFORE React.
This will completely disable React-Devtools. When you click on the devtools tab it will just say 'Looking for React...'.

Okay, found a way to keep even that function from getting injected/involved.
I just changed the disabler code to this:
// disable react-dev-tools for this project
if (typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__ === "object") {
for (let [key, value] of Object.entries(window.__REACT_DEVTOOLS_GLOBAL_HOOK__)) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__[key] = typeof value == "function" ? ()=>{} : null;
}
}

Related

SendBird create GroupChannel with file as cover

I'm trying to create a group Channel with a cover photo,
this.sendBirdInstance.GroupChannel.createChannelWithUserIds(userIds, true, this.groupName, this.groupPhotoFile, '', function (createdChannel, error) {
...
}
According to the documentation I can add a url or a file
coverUrl : the file or URL of the cover image, which you can fetch to
render into the UI.
But when adding a file, I'm always getting : "SendBirdException", code: 800110, message: "Invalid arguments."
Is there a way to create a group with a file instead of a url (since I want the user to upload the file) ?
Thanks,
As you have already experienced (I saw that you have created an issue in GitHub several days ago) the support of SendBird is kinda unreliable.
The fact that they offer just a minified version of their JavaScript SDK (which personally I find very poor) is helping either.
Anyway, I could isolate the createChannelWithUserIds function:
! function (e, n) {
// ...
}(this, function () {
// ...
var h = function (e) {
for (var n in e) if (e.hasOwnProperty(n)) return !1;
return Array.isArray(e) ? JSON.stringify(e) === JSON.stringify([]) : JSON.stringify(e) === JSON.stringify({})
},
// ...
A = function () { // it returns SendBird function
// ...
var w = function (e) { // w is this.GroupChannel
// ...
w.createChannelWithUserIds = function () {
// ...
// here comes the param validation (I've added spaces for a better lecture):
if (!Array.isArray(e) || "boolean" != typeof n || "string" != typeof t && null !== t && void 0 !== t || "string" != typeof r && h(r) && null !== r && void 0 !== r || "string" != typeof a && null !== a && void 0 !== a || "string" != typeof i && null !== i && void 0 !== i) return void U(null, new p("Invalid arguments.", J.INVALID_PARAMETER), s);
// It will return "Invalid arguments." if any of the conditions evaluates to true
// ...
}
}
}
return function () {
// ...
}().SendBird
});
You are using the function like this:
createChannelWithUserIds(o, n, t, r, a, s);
So the fourth parameter (r) is coverURL: the file with the cover picture (this.groupPhotoFile);
Its validation is basically saying that:
"string" != typeof r // if `r` is not a string (a URL)
&& h(r) // and the returned value of function h(r) is true
&& null !== r // and it is not null
&& void 0 !== r // and it is not undefined
that parameter is invalid.
Your file is not a string, not null and not undefined, so everything boils down to the h() function:
var h = function (e) {
for (var n in e) if (e.hasOwnProperty(n)) return !1;
return Array.isArray(e) ? JSON.stringify(e) === JSON.stringify([]) : JSON.stringify(e) === JSON.stringify({})
}
The function above check in first place if the object has any property what is a member of the object itself (i.e. not properties belonging to the prototype chain).
Then, if it doesn't have any own property, it checks if the stringify object/array is equal to an empty object/array.
I can't say what is the intention of the developers when validating files though this function, but a standard FILE object:
has properties in its prototype chain, but not directly assigned to the instance, so the first condition is true.
when stringify returns a empty object in all major browsers nowadays (it was not always like so), so the second condition is also true.
As we saw before, we need h() to return false: the validation fails if it returns true.
To fix this behavior you could change the h() function to something like:
var h = function( e ){
return !(e instanceof File);
// or return e.constructor != File;
// or return Object.getPrototypeOf( e ) != File.prototype );
// or return e.__proto__ != File.prototype )
// or return e.constructor.prototype != File.prototype )
}
but I won't mess with it. It could be used in future versions with a different purpose.
So your best bet is to change the createChannelWithUserIds() function to:
remove the call to the h() function from the validation.
replace it with a call to your own file validation
To do that, you could override the function in a SendBird instance:
var sb = new SendBird( { appId: ... } );
sb.GroupChannel.createChannelWithUserIds = function(){ ... };
But it is not guarantee to work and could break in future releases, so I would just edit the SendBird.min.js file.
In other words, replace:
if(!Array.isArray(e)||"boolean"!=typeof n||"string"!=typeof t&&null!==t&&void 0!==t||"string"!=typeof r&&h(r)&&null!==r&&void 0!==r||"string"!=typeof a&&null!==a&&void 0!==a||"string"!=typeof i&&null!==i&&void 0!==i)return void U(null,new p("Invalid arguments.",J.INVALID_PARAMETER),s);
with:
if(!Array.isArray(e)||"boolean"!=typeof n||"string"!=typeof t&&null!==t&&void 0!==t||"string"!=typeof a&&null!==a&&void 0!==a||"string"!=typeof i&&null!==i&&void 0!==i)return void U(null,new p("Invalid arguments.",J.INVALID_PARAMETER),s);
In the current version (v3.0.41) you will find two coincidences of the code above: one for createChannel and other for createChannelWithUserIds, you can replace both.
Of course, editing the .js file is annoying because you will need to take care to replace the code everytime you upgrade SendGrid.
You could create an automatic task inside your CI pipeline to do it for you, thought.
Hopefully, the SendGrid developers will acknowledge your issue and fix it in a future release.

How do I correctly manage multi-level proxying of objects

As demonstrated in this SO question, Proxy objects are a good way to watch for changes in an object.
What if you want to watch changes to subobjects? You'd need to proxy those subobjects as well.
I'm currently working with some code that does that automatically - any time you try to set a property it makes sure that it is wrapped in a proxy. We're doing this so that we can perform an action every time any value changes.
function createProxiedObject(objToProxy) {
return new Proxy(objToProxy, {
set: function (target, key, value) {
//proxy nested objects
if (value !== null && typeof value === 'object') {
value = createProxiedObject(value);
}
target[key.toString()] = value;
handleProxiedObjectChange();
});
This works pretty well, but there is at least one case in which this backfires:
function ensureObjectHasProperty(object, key, default) {
if (object[key] !== null) {
// some validation happens here
return object[key];
} else {
return default;
}
}
...
proxiedObject = somethingThatCreatesAProxiedObject(someValue);
proxiedObject[someProperty] = ensureObjectHasProperty(proxiedObject, someProperty, defaultValue)
The result is that the value under someProperty (which is already being proxied) gets reassigned to the proxied object, causing it to get wrapped in another proxy. This results in the handleProxiedObjectChange method being called more than once each time any part of the object changes.
The simple way to fix it is to never assign anything to the proxied object unless it's new, but as this problem has already happened there's a reasonable chance someone will do it again in the future. How can I fix the set function to not rewrap objects that are already being proxied? Or is there a better way to watch an object so that handleProxiedObjectChange can be called any time the object or any of its subobjects change?
As suggested by #DecentDabbler, using a WeakSet allowed me to ensure I never try to wrap a proxy in another proxy:
const proxiedObjs = new WeakSet();
...
function createProxiedObject(objToProxy) {
// Recursively ensure object is deeply proxied
for (let x in objToProxy) {
subObj = objToProxy[x];
if (subObj !== null && typeof subObj === 'object' && !proxiedObjs.has(subObj)) {
objToProxy[x] = createProxiedObject(subObj);
}
}
let proxied = new Proxy(objToProxy, {
set: function (target, key, value) {
//This check is also new - if nothing actually changes
//I'd rather not call handleProxiedObjectChange
if (_.isEqual(target[key.toString()], value)) {
return true;
}
//proxy nested objects
if (value !== null && typeof value === 'object' && !proxiedObjs.has(value)) {
value = createProxiedObject(value);
}
target[key.toString()] = value;
handleProxiedObjectChange();
});
proxiedObjs.add(proxied);
return proxied;

Site behaves differently when developer tools are open IE11

Im using the following template in IE11 and can not figure out why the sidebar sings in every time navigation is happening. When developer tools are open it behaves as I would like it to. It is easily demoed by clicking on any one of the tabs under UI element in the tree while running IE11. However you will notice if F12 developer tools are open the side bar does not slide in every time navigation happens. This is not an issue in chrome. There is an error with fastclick that may show up however I have ran without fastclick and it still happens. Any help would be great. Thanks.
https://almsaeedstudio.com/themes/AdminLTE/pages/UI/general.html
Try removing any console.log() from your code.
console.log() which is to help out when debugging Javascript can cause IE to completely stop processing scripts on the page. To add to the mystery, if you keep watching your page in IE with devtools open - you won’t notice an issue at all.
Explanation
The reason for this is the console object is not instantiated unless devtools is open in IE. Otherwise, you will see one of two things:
Javascript won’t execute correctly
Console has cryptic errors, such as ‘object is undefined’ or others of that nature
Nine times out of ten, you have an errant console.log in the code somewhere. This does not affect any browser other than IE.
Another potential cause, especially if you are performing ajax calls, is the ajax response may be cached when dev tools are closed, but refreshed from the server when dev tools are open.
In IE, open the Network tab of Developer Tools, click the play icon, and un-set the Always refresh from server button. Then watch to see if any of your ajax calls are coming back with a response code of 304 (Not modified). If they are, then you are not getting fresh data from the server and you need to update the cacheability settings on the page that is being called via ajax.
Adding onto the already great answers (since I can't comment - requires 50 rep points), agreeing with the answer from #sam100rav and the comment from #storsoc, I discovered that in IE11 version 11.1387.15063.0 with updated version 11.0.90 (KB4462949), that window.console indeed exists as an empty object (window.console = {}). Hence, I used a variation of the polyfill from #storsoc as shown below.
if (!window.console || Object.keys(window.console).length === 0) {
window.console = {
log: function() {},
info: function() {},
error: function() {},
warn: function() {}
};
}
As pointed out already it's because IE11 + Edge<=16 is so stupid that it doesn't support console unless developer tools is opened... So if you open that to disable caching you won't see any issues and you might think that the issue was just due to browser cache... but nope.. :#
I made this "polyfill" for it (doesn't really polyfill, but makes IE not throw any errors). Add it as early as possible on your site as any js might be using console.log or console.warn etc.
window.console = typeof window.console !== 'object' || {};
console.warn = typeof console.warn === 'function' || function () {
return this;
};
console.log = typeof console.log === 'function' || function () {
return this;
};
console.info = typeof console.info === 'function' || function () {
return this;
};
console.error = typeof console.error === 'function' || function () {
return this;
};
console.assert = typeof console.assert === 'function' || function () {
return this;
};
console.dir = typeof console.dir === 'function' || function () {
return this;
};
console.table = typeof console.table === 'function' || function () {
return this;
};
console.group = typeof console.group === 'function' || function () {
return this;
};
console.groupEnd = typeof console.groupEnd === 'function' || function () {
return this;
};
console.time = typeof console.time === 'function' || function () {
return this;
};
console.timeEnd = typeof console.timeEnd === 'function' || function () {
return this;
};
console.timeLog = typeof console.timeLog === 'function' || function () {
return this;
};
console.trace = typeof console.trace === 'function' || function () {
return this;
};
console.clear = typeof console.clear === 'function' || function () {
return this;
};
console.count = typeof console.count === 'function' || function () {
return this;
};
console.debug = typeof console.debug === 'function' || function () {
return this;
};
console.dirxml = typeof console.dirxml === 'function' || function () {
return this;
};
console.groupCollapsed = typeof console.groupCollapsed === 'function' || function () {
return this;
};
I'm assuming you've fixed this since you posted it as I can not see the behavior you describe in your link.
However, I have recently run into a similar issue where the dev tools being open changed the behavior not because of console issues, but because opening the tools changed the width of the window. It was the window width difference that triggered an underlying bug in my case.
Related post here.
It's possible you've got the compatibility mode set to a later version of IE in your developer console (see the highlighted section)

How to check 'undefined' value in jQuery

Possible Duplicate:
Detecting an undefined object property in JavaScript
javascript undefined compare
How we can add a check for an undefined variable, like:
function A(val) {
if (val == undefined)
// do this
else
// do this
}
JQuery library was developed specifically to simplify and to unify certain JavaScript functionality.
However if you need to check a variable against undefined value, there is no need to invent any special method, since JavaScript has a typeof operator, which is simple, fast and cross-platform:
if (typeof value === "undefined") {
// ...
}
It returns a string indicating the type of the variable or other unevaluated operand. The main advantage of this method, compared to if (value === undefined) { ... }, is that typeof will never raise an exception in case if variable value does not exist.
In this case you can use a === undefined comparison: if(val === undefined)
This works because val always exists (it's a function argument).
If you wanted to test an arbitrary variable that is not an argument, i.e. might not be defined at all, you'd have to use if(typeof val === 'undefined') to avoid an exception in case val didn't exist.
Note that typeof always returns a string, and doesn't generate an error if the variable doesn't exist at all.
function A(val){
if(typeof(val) === "undefined")
//do this
else
//do this
}
I know I am late to answer the function but jquery have a in build function to do this
if(jQuery.type(val) === "undefined"){
//Some code goes here
}
Refer jquery API document of jquery.type https://api.jquery.com/jQuery.type/ for the same.
You can use shorthand technique to check whether it is undefined or null
function A(val)
{
if(val || "")
//do this
else
//do this
}
hope this will help you
when I am testing "typeof obj === undefined", the alert(typeof obj) returning object, even though obj is undefined.
Since obj is type of Object its returning Object, not undefined.
So after hours of testing I opted below technique.
if(document.getElementById(obj) !== null){
//do...
}else{
//do...
}
I am not sure why the first technique didn't work.But I get done my work using this.
If you have names of the element and not id we can achieve the undefined check on all text elements (for example) as below and fill them with a default value say 0.0:
var aFieldsCannotBeNull=['ast_chkacc_bwr','ast_savacc_bwr'];
jQuery.each(aFieldsCannotBeNull,function(nShowIndex,sShowKey) {
var $_oField = jQuery("input[name='"+sShowKey+"']");
if($_oField.val().trim().length === 0){
$_oField.val('0.0')
}
})
I am not sure it is the best solution, but it works fine:
if($someObject['length']!=0){
//do someting
}
function isValue(value, def, is_return) {
if ( $.type(value) == 'null'
|| $.type(value) == 'undefined'
|| $.trim(value) == ''
|| ($.type(value) == 'number' && !$.isNumeric(value))
|| ($.type(value) == 'array' && value.length == 0)
|| ($.type(value) == 'object' && $.isEmptyObject(value)) ) {
return ($.type(def) != 'undefined') ? def : false;
} else {
return ($.type(is_return) == 'boolean' && is_return === true ? value : true);
}
}
try this~ all type checker
Check if undefined or not
if(typeof myVal === "undefined") {
//some code
}
Check if undefined or null or empty or false or 0
if(!myVal) {
// some code
} else {
// myVal is flawless
}

How do you check if a JavaScript Object is a DOM Object?

I'm trying to get:
document.createElement('div') //=> true
{tagName: 'foobar something'} //=> false
In my own scripts, I used to just use this since I never needed tagName as a property:
if (!object.tagName) throw ...;
So for the second object, I came up with the following as a quick solution -- which mostly works. ;)
The problem is, it depends on browsers enforcing read-only properties, which not all do.
function isDOM(obj) {
var tag = obj.tagName;
try {
obj.tagName = ''; // Read-only for DOM, should throw exception
obj.tagName = tag; // Restore for normal objects
return false;
} catch (e) {
return true;
}
}
Is there a good substitute?
This might be of interest:
function isElement(obj) {
try {
//Using W3 DOM2 (works for FF, Opera and Chrome)
return obj instanceof HTMLElement;
}
catch(e){
//Browsers not supporting W3 DOM2 don't have HTMLElement and
//an exception is thrown and we end up here. Testing some
//properties that all elements have (works on IE7)
return (typeof obj==="object") &&
(obj.nodeType===1) && (typeof obj.style === "object") &&
(typeof obj.ownerDocument ==="object");
}
}
It's part of the DOM, Level2.
Update 2: This is how I implemented it in my own library:
(the previous code didn't work in Chrome, because Node and HTMLElement are functions instead of the expected object. This code is tested in FF3, IE7, Chrome 1 and Opera 9).
//Returns true if it is a DOM node
function isNode(o){
return (
typeof Node === "object" ? o instanceof Node :
o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
);
}
//Returns true if it is a DOM element
function isElement(o){
return (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
);
}
The accepted answer is a bit complicated, and does not detect all types of HTML elements. For example, SVG elements are not supported. In contrast, this answer works for HTML as well as SVG, etc.
See it in action here: https://jsfiddle.net/eLuhbu6r/
function isElement(element) {
return element instanceof Element || element instanceof HTMLDocument;
}
Cherry on top: the above code is IE8 compatible.
No need for hacks, you can just ask if an element is an instance of the DOM Element:
const isDOM = el => el instanceof Element
All solutions above and below (my solution including) suffer from possibility of being incorrect, especially on IE — it is quite possible to (re)define some objects/methods/properties to mimic a DOM node rendering the test invalid.
So usually I use the duck-typing-style testing: I test specifically for things I use. For example, if I want to clone a node I test it like this:
if(typeof node == "object" && "nodeType" in node &&
node.nodeType === 1 && node.cloneNode){
// most probably this is a DOM node, we can clone it safely
clonedNode = node.cloneNode(false);
}
Basically it is a little sanity check + the direct test for a method (or a property) I am planning to use.
Incidentally the test above is a good test for DOM nodes on all browsers. But if you want to be on the safe side always check the presence of methods and properties and verify their types.
EDIT: IE uses ActiveX objects to represent nodes, so their properties do not behave as true JavaScript object, for example:
console.log(typeof node.cloneNode); // object
console.log(node.cloneNode instanceof Function); // false
while it should return "function" and true respectively. The only way to test methods is to see if the are defined.
A simple way to test if a variable is a DOM element (verbose, but more traditional syntax :-)
function isDomEntity(entity) {
if(typeof entity === 'object' && entity.nodeType !== undefined){
return true;
}
else{
return false;
}
}
Or as HTMLGuy suggested (short and clean syntax):
const isDomEntity = entity =>
typeof entity === 'object' && entity.nodeType !== undefined
You could try appending it to a real DOM node...
function isDom(obj)
{
var elm = document.createElement('div');
try
{
elm.appendChild(obj);
}
catch (e)
{
return false;
}
return true;
}
How about Lo-Dash's _.isElement?
$ npm install lodash.iselement
And in the code:
var isElement = require("lodash.iselement");
isElement(document.body);
This is from the lovely JavaScript library MooTools:
if (obj.nodeName){
switch (obj.nodeType){
case 1: return 'element';
case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
}
}
The using the root detection found here, we can determine whether e.g. alert is a member of the object's root, which is then likely to be a window:
function isInAnyDOM(o) {
return (o !== null) && !!(o.ownerDocument && (o.ownerDocument.defaultView || o.ownerDocument.parentWindow).alert); // true|false
}
To determine whether the object is the current window is even simpler:
function isInCurrentDOM(o) {
return (o !== null) && !!o.ownerDocument && (window === (o.ownerDocument.defaultView || o.ownerDocument.parentWindow)); // true|false
}
This seems to be less expensive than the try/catch solution in the opening thread.
Don P
old thread, but here's an updated possibility for ie8 and ff3.5 users:
function isHTMLElement(o) {
return (o.constructor.toString().search(/\object HTML.+Element/) > -1);
}
var IsPlainObject = function ( obj ) { return obj instanceof Object && ! ( obj instanceof Function || obj.toString( ) !== '[object Object]' || obj.constructor.name !== 'Object' ); },
IsDOMObject = function ( obj ) { return obj instanceof EventTarget; },
IsDOMElement = function ( obj ) { return obj instanceof Node; },
IsListObject = function ( obj ) { return obj instanceof Array || obj instanceof NodeList; },
// In fact I am more likely t use these inline, but sometimes it is good to have these shortcuts for setup code
I think prototyping is not a very good solution but maybe this is the fastest one:
Define this code block;
Element.prototype.isDomElement = true;
HTMLElement.prototype.isDomElement = true;
than check your objects isDomElement property:
if(a.isDomElement){}
I hope this helps.
This could be helpful: isDOM
//-----------------------------------
// Determines if the #obj parameter is a DOM element
function isDOM (obj) {
// DOM, Level2
if ("HTMLElement" in window) {
return (obj && obj instanceof HTMLElement);
}
// Older browsers
return !!(obj && typeof obj === "object" && obj.nodeType === 1 && obj.nodeName);
}
In the code above, we use the double negation operator to get the boolean value of the object passed as argument, this way we ensure that each expression evaluated in the conditional statement be boolean, taking advantage of the Short-Circuit Evaluation, thus the function returns true or false
According to mdn
Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements.
We can implement isElement by prototype. Here is my advice:
/**
* #description detect if obj is an element
* #param {*} obj
* #returns {Boolean}
* #example
* see below
*/
function isElement(obj) {
if (typeof obj !== 'object') {
return false
}
let prototypeStr, prototype
do {
prototype = Object.getPrototypeOf(obj)
// to work in iframe
prototypeStr = Object.prototype.toString.call(prototype)
// '[object Document]' is used to detect document
if (
prototypeStr === '[object Element]' ||
prototypeStr === '[object Document]'
) {
return true
}
obj = prototype
// null is the terminal of object
} while (prototype !== null)
return false
}
console.log(isElement(document)) // true
console.log(isElement(document.documentElement)) // true
console.log(isElement(document.body)) // true
console.log(isElement(document.getElementsByTagName('svg')[0])) // true or false, decided by whether there is svg element
console.log(isElement(document.getElementsByTagName('svg'))) // false
console.log(isElement(document.createDocumentFragment())) // false
I think that what you have to do is make a thorough check of some properties that will always be in a dom element, but their combination won't most likely be in another object, like so:
var isDom = function (inp) {
return inp && inp.tagName && inp.nodeName && inp.ownerDocument && inp.removeAttribute;
};
In Firefox, you can use the instanceof Node. That Node is defined in DOM1.
But that is not that easy in IE.
"instanceof ActiveXObject" only can tell that it is a native object.
"typeof document.body.appendChild=='object'" tell that it may be DOM object, but also can be something else have same function.
You can only ensure it is DOM element by using DOM function and catch if any exception. However, it may have side effect (e.g. change object internal state/performance/memory leak)
Perhaps this is an alternative? Tested in Opera 11, FireFox 6, Internet Explorer 8, Safari 5 and Google Chrome 16.
function isDOMNode(v) {
if ( v===null ) return false;
if ( typeof v!=='object' ) return false;
if ( !('nodeName' in v) ) return false;
var nn = v.nodeName;
try {
// DOM node property nodeName is readonly.
// Most browsers throws an error...
v.nodeName = 'is readonly?';
} catch (e) {
// ... indicating v is a DOM node ...
return true;
}
// ...but others silently ignore the attempt to set the nodeName.
if ( v.nodeName===nn ) return true;
// Property nodeName set (and reset) - v is not a DOM node.
v.nodeName = nn;
return false;
}
Function won't be fooled by e.g. this
isDOMNode( {'nodeName':'fake'} ); // returns false
You can see if the object or node in question returns a string type.
typeof (array).innerHTML === "string" => false
typeof (object).innerHTML === "string" => false
typeof (number).innerHTML === "string" => false
typeof (text).innerHTML === "string" => false
//any DOM element will test as true
typeof (HTML object).innerHTML === "string" => true
typeof (document.createElement('anything')).innerHTML === "string" => true
This is what I figured out:
var isHTMLElement = (function () {
if ("HTMLElement" in window) {
// Voilà. Quick and easy. And reliable.
return function (el) {return el instanceof HTMLElement;};
} else if ((document.createElement("a")).constructor) {
// We can access an element's constructor. So, this is not IE7
var ElementConstructors = {}, nodeName;
return function (el) {
return el && typeof el.nodeName === "string" &&
(el instanceof ((nodeName = el.nodeName.toLowerCase()) in ElementConstructors
? ElementConstructors[nodeName]
: (ElementConstructors[nodeName] = (document.createElement(nodeName)).constructor)))
}
} else {
// Not that reliable, but we don't seem to have another choice. Probably IE7
return function (el) {
return typeof el === "object" && el.nodeType === 1 && typeof el.nodeName === "string";
}
}
})();
To improve performance I created a self-invoking function that tests the browser's capabilities only once and assigns the appropriate function accordingly.
The first test should work in most modern browsers and was already discussed here. It just tests if the element is an instance of HTMLElement. Very straightforward.
The second one is the most interesting one. This is its core-functionality:
return el instanceof (document.createElement(el.nodeName)).constructor
It tests whether el is an instance of the construcor it pretends to be. To do that, we need access to an element's contructor. That's why we're testing this in the if-Statement. IE7 for example fails this, because (document.createElement("a")).constructor is undefined in IE7.
The problem with this approach is that document.createElement is really not the fastest function and could easily slow down your application if you're testing a lot of elements with it. To solve this, I decided to cache the constructors. The object ElementConstructors has nodeNames as keys with its corresponding constructors as values. If a constructor is already cached, it uses it from the cache, otherwise it creates the Element, caches its constructor for future access and then tests against it.
The third test is the unpleasant fallback. It tests whether el is an object, has a nodeType property set to 1 and a string as nodeName. This is not very reliable of course, yet the vast majority of users shouldn't even fall back so far.
This is the most reliable approach I came up with while still keeping performance as high as possible.
Test if obj inherits from Node.
if (obj instanceof Node){
// obj is a DOM Object
}
Node is a basic Interface from which HTMLElement and Text inherit.
For the ones using Angular:
angular.isElement
https://docs.angularjs.org/api/ng/function/angular.isElement
This will work for almost any browser. (No distinction between elements and nodes here)
function dom_element_check(element){
if (typeof element.nodeType !== 'undefined'){
return true;
}
return false;
}
differentiate a raw js object from a HTMLElement
function isDOM (x){
return /HTML/.test( {}.toString.call(x) );
}
use:
isDOM( {a:1} ) // false
isDOM( document.body ) // true
// OR
Object.defineProperty(Object.prototype, "is",
{
value: function (x) {
return {}.toString.call(this).indexOf(x) >= 0;
}
});
use:
o={}; o.is("HTML") // false
o=document.body; o.is("HTML") // true
here's a trick using jQuery
var obj = {};
var element = document.getElementById('myId'); // or simply $("#myId")
$(obj).html() == undefined // true
$(element).html() == undefined // false
so putting it in a function:
function isElement(obj){
return (typeOf obj === 'object' && !($(obj).html() == undefined));
}
Not to hammer on this or anything but for ES5-compliant browsers why not just:
function isDOM(e) {
return (/HTML(?:.*)Element/).test(Object.prototype.toString.call(e).slice(8, -1));
}
Won't work on TextNodes and not sure about Shadow DOM or DocumentFragments etc. but will work on almost all HTML tag elements.
A absolute right method, check target is a real html element
primary code:
(function (scope) {
if (!scope.window) {//May not run in window scope
return;
}
var HTMLElement = window.HTMLElement || window.Element|| function() {};
var tempDiv = document.createElement("div");
var isChildOf = function(target, parent) {
if (!target) {
return false;
}
if (parent == null) {
parent = document.body;
}
if (target === parent) {
return true;
}
var newParent = target.parentNode || target.parentElement;
if (!newParent) {
return false;
}
return isChildOf(newParent, parent);
}
/**
* The dom helper
*/
var Dom = {
/**
* Detect if target element is child element of parent
* #param {} target The target html node
* #param {} parent The the parent to check
* #returns {}
*/
IsChildOf: function (target, parent) {
return isChildOf(target, parent);
},
/**
* Detect target is html element
* #param {} target The target to check
* #returns {} True if target is html node
*/
IsHtmlElement: function (target) {
if (!X.Dom.IsHtmlNode(target)) {
return false;
}
return target.nodeType === 1;
},
/**
* Detect target is html node
* #param {} target The target to check
* #returns {} True if target is html node
*/
IsHtmlNode:function(target) {
if (target instanceof HTMLElement) {
return true;
}
if (target != null) {
if (isChildOf(target, document.documentElement)) {
return true;
}
try {
tempDiv.appendChild(target.cloneNode(false));
if (tempDiv.childNodes.length > 0) {
tempDiv.innerHTML = "";
return true;
}
} catch (e) {
}
}
return false;
}
};
X.Dom = Dom;
})(this);
Each DOMElement.constructor returns function HTML...Element() or [Object HTML...Element] so...
function isDOM(getElem){
if(getElem===null||typeof getElem==="undefined") return false;
var c = getElem.constructor.toString();
var html = c.search("HTML")!==-1;
var element = c.search("Element")!==-1;
return html&&element;
}
I have a special way to do this that has not yet been mentioned in the answers.
My solution is based on four tests. If the object passes all four, then it is an element:
The object is not null.
The object has a method called "appendChild".
The method "appendChild" was inherited from the Node class, and isn't just an imposter method (a user-created property with an identical name).
The object is of Node Type 1 (Element). Objects that inherit methods from the Node class are always Nodes, but not necessarily Elements.
Q: How do I check if a given property is inherited and isn't just an imposter?
A: A simple test to see if a method was truly inherited from Node is to first verify that the property has a type of "object" or "function". Next, convert the property to a string and check if the result contains the text "[Native Code]". If the result looks something like this:
function appendChild(){
[Native Code]
}
Then the method has been inherited from the Node object. See https://davidwalsh.name/detect-native-function
And finally, bringing all the tests together, the solution is:
function ObjectIsElement(obj) {
var IsElem = true;
if (obj == null) {
IsElem = false;
} else if (typeof(obj.appendChild) != "object" && typeof(obj.appendChild) != "function") {
//IE8 and below returns "object" when getting the type of a function, IE9+ returns "function"
IsElem = false;
} else if ((obj.appendChild + '').replace(/[\r\n\t\b\f\v\xC2\xA0\x00-\x1F\x7F-\x9F ]/ig, '').search(/\{\[NativeCode]}$/i) == -1) {
IsElem = false;
} else if (obj.nodeType != 1) {
IsElem = false;
}
return IsElem;
}
(element instanceof $ && element.get(0) instanceof Element) || element instanceof Element
This will check for even if it is a jQuery or JavaScript DOM element
The only way to guarentee you're checking an actual HTMLEement, and not just an object with the same properties as an HTML Element, is to determine if it inherits from Node, since its impossible to make a new Node() in JavaScript. (unless the native Node function is overwritten, but then you're out of luck). So:
function isHTML(obj) {
return obj instanceof Node;
}
console.log(
isHTML(test),
isHTML(ok),
isHTML(p),
isHTML(o),
isHTML({
constructor: {
name: "HTML"
}
}),
isHTML({
__proto__: {
__proto__: {
__proto__: {
__proto__: {
constructor: {
constructor: {
name: "Function"
},
name: "Node"
}
}
}
}
}
}),
)
<div id=test></div>
<blockquote id="ok"></blockquote>
<p id=p></p>
<br id=o>
<!--think of anything else you want--!>

Categories

Resources