Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Пища для ума (https://javascript.ru/forum/misc/67857-pishha-dlya-uma.html)

exec 12.03.2017 03:42

Пища для ума
 
Задача чисто на интерес: можно ли написать функцию, которая проверяет является ли объект DOM-элементом, и которую невозможно обмануть, даже нарочно?

Проверять наличие tagName, nodeType не вариант - очень легко подать обычный объект: var fake = { tagName : "DIV" }

Такая функция:

function isDOMElement(target) {
	try {
		document.createDocumentFragment().appendChild(target.cloneNode());
	} catch (e) {
		return false;
	}
	return true;
}


выглядит неплохо, но её можно обмануть, подав такой объект: var fake = { cloneNode : function () { return document.createElement("DIV"); } }

Другой вариант:

function isDOMElement(target) {
	try {
		target.parentNode.insertBefore(target, target);
	} catch (e) {
		return false;
	}
	return true;
}


Её уже не провести, т.к. нативный insertBefore бросит исключение.
Но, к сожалению, не работает с новосозданными элементами, ведь у них нет parentNode:

isDOMElement( document.createElement("DIV") ); // false


Так как же быть?

exec 12.03.2017 03:50

Пока что есть такое:

function isDOMElement(target) {
	try {
		target.parentNode.insertBefore(target, target);
	} catch (e) {
		try {
			var parent = document.createDocumentFragment();
			parent.appendChild(target);
		} catch (e) {
			return false;
		}
		parent.removeChild(target);
		return true;
	}
	return true;
}


var elem1 = document.createElement("A");
var elem2 = document.documentElement;
var elem3 = {};
alert( [isDOMElement(elem1), isDOMElement(elem2), isDOMElement(elem3)] ); // true,true,false


Можно ли как-то проще?
Ещё кроссбраузерность под вопросом, у меня в опере true, true, false

upd: разумеется, важно никак не менять структуру DOM, отсюда фокусы с insertBefore и appendChild/removeChild

рони 12.03.2017 04:03

exec,
а так ?
function isDOMElement(target) {
  return target.ownerDocument == document
}

exec 12.03.2017 04:11

function isDOMElement(target) {
  return target.ownerDocument == document
}

alert( isDOMElement({ ownerDocument : document }) );

nerv_ 12.03.2017 09:48

function isDOMElement(any) {
	return any instanceof HTMLElement ||
  			any instanceof HTMLDocument
}

console.assert(isDOMElement(document), 'document')
console.assert(isDOMElement(document.body), 'document.body')
console.assert(!isDOMElement({}), '{}')


Если делать
let proto = Object.create(HTMLElement.prototype)
// or
let proto2 = {__proto__: HTMLElement.prototype}

то данный объект по определению будет являться элементом, т.к. у него прототип элемента

У меня еще несколько вариантов есть, но пока их раскрывать не стану)

exec 12.03.2017 17:53

Цитата:

Сообщение от nerv_ (Сообщение 447098)
то данный объект по определению будет являться элементом, т.к. у него прототип элемента

С ним нельзя будет работать как с DOM-элементом, например добавить его на страницу через appendChild. Так что это не DOM-элемент, а всего лишь объект с прототипом DOM-элемента.

nerv_ 12.03.2017 22:11

признаться, мне не понятно, в чем интерес данной задачи. Похоже на игру в интерпретатор: что верент этот код (и написан какой-нибудь треш).

Тем не менее, можно так поробовать
function isDOMElement(any) {
	return any instanceof Node && 
  			(any instanceof HTMLElement ||
  			 any instanceof HTMLDocument)
}

console.assert(isDOMElement(document), 'document')
console.assert(isDOMElement(document.body), 'document.body')
console.assert(isDOMElement(document.createElement('div')), 'document.createElement(\'div\')')
console.assert(!isDOMElement(Object.create(Node.prototype)), 'Object.create(Node.prototype)')


Часовой пояс GMT +3, время: 09:35.