Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Доступ к элементу после модификации HTML inner и outer (https://javascript.ru/forum/misc/73991-dostup-k-ehlementu-posle-modifikacii-html-inner-i-outer.html)

xShift 03.06.2018 19:47

Доступ к элементу после модификации HTML inner и outer
 
В общем пилил запиливатель красивых элементов форм на чистом JS и нашел угарный баг браузеров.

Lets look:

let test = document.querySelectorAll('#tabs');

	test[0].parentElement.parentElement.outerHTML = '<div class="zalupa">'+ test[0].parentElement.parentElement.outerHTML +'</div>';

	console.log( document.querySelectorAll('#logotype')[0].closest('#mainHeader') ); // work

	console.log( test[0].parentElement.parentElement.parentElement ); // zalupa null

	console.log( test[0].closest('.zalupa') );	// zalupa null


<div id="shell">
		<header id="mainHeader">
			<h1 id="logotype">
				<a href="#" data-title="and add">adds</a> 
				:: <span>asdasdasdsad</span></h1>
		</header>
	</div><!--#shell-->

	<div id="testElements">
		
		<section style="text-align: center; width: 980px; margin: 0 auto">
			<aside id="tabs" class="tabs">
				<ul>
					<li data-link="tab-1">1</li>
					<li class="activetab" data-link="tab-2">2</li>
					<li data-link="tab-3">3</li>
				</ul>

				<div data-content="tab-1">Tous mes rêves se réalisent.</div>

				<div class="tabactive" data-content="tab-2">Si on vit sans but, on mourra pour rien.</div>

				<div data-content="tab-3">Jouis de chaque moment.</div>

			</aside>
		</section>
</div>



В общем долго не понимал почему нет доступа к залупе :haha: и почему у меня не работает unwrap . Оказывается залупа не добавляется в какой-то там node-list и приходится каждый раз получать через querySelector.

И тогда я решил создавать залупу не строчно, но c помощью запиленной в браузер функциональности:

let test = document.querySelectorAll('#tabs');

	let zalupa = document.createElement('div');

	zalupa.classList.add('zalupa');

	zalupa.innerHTML = test[0].parentElement.parentElement.outerHTML;

	test[0].parentElement.parentElement.outerHTML = zalupa.outerHTML

	console.log(test[0].closest('.zalupa')); // zalupa null


Таким образом выяснилось, что даже так залупа не появляется, а кроме innerHTML и outerHTML я средств манипуляции содержимым не знаю.

Может кто-то знает более изящный и простой способ манипулировать такими данными, чтобы они потом были доступны в next, previous node list и конечно closest или parentElement? Не охота мне писать createElement вообще ибо это расточительство.

Nexus 04.06.2018 10:41

Цитата:

Сообщение от xShift
и нашел угарный баг браузеров.

Если что-то работает не так, как вам хочется - это не значит, что это баг.

В первом варианте вы сохраняете в переменную test ссылку на DOM элемент, после изменяете outerHTML родителя. После внесения изменений ветка измененного родителя "перестраивается" и ваш test указывает уже на несуществующий элемент.

Во втором случае все почти тоже самое, только вы родителю "скармливаете" outerHTML виртуального DOM элемента.

xShift 04.06.2018 10:58

Nexus, так а решить то как по другому?

Nexus 04.06.2018 11:06

Цитата:

Сообщение от xShift
Nexus, так а решить то как по другому?

Я бы представление изменил или нашел решение без оборачивания элементов.

Alexandroppolus 04.06.2018 11:39

Если не менять логику с элементами, то можно вот так

var test = document.querySelectorAll('#tabs');
var parent = test[0].parentElement.parentElement;
var newParent = document.createElement('div');
newParent.className = 'zalupa';
parent.parentNode.replaceChild(newParent, parent);
newParent.appendChild(parent);


при этом все объекты-элементы сохраняются.

xShift 04.06.2018 12:25

Alexandroppolus, хм тоже выход оказывается. Но к сожалению не получается сделать без обезьяньих скачек по дереву разными методами.

ruslan_mart 04.06.2018 13:02

xShift, потому что нужно правильно оформлять HTML разметку, чтобы было удобно манипулировать DOM-элементами. Лично у меня, таких извращений не было с элементами, хотя я миллион раз писал различные модули табов, форм, модалок и т.д., на чистом JS. Просто пересмотрите свой подход. Даже не могу представить, в каком случае и при каком действии может понадобиться обернуть элемент в другой. Разметка должна быть строгой, а для манимуляции с DOM - вполне должно быть достаточно добавление/удаление новых элементов в каком-либо блоке, или изменение className. Все эти методы, типа wrap и т.д. - это чистой воды извращение, такого быть не должно и не должно возникать такой нужды при грамотном подходе к разметке и скриптингу в целом!

xShift 04.06.2018 15:41

ruslan_mart, окей, пишем стилизатор select. Как вы без замены стандартных options и их контейнера это сделаете без wrap. Вы конечно правы но бывают ситуации , когда в движек лезти и менять разметку нецелесообразно.

Nexus 04.06.2018 15:46

Цитата:

Сообщение от xShift
Как вы без замены стандартных options и их контейнера это сделаете без wrap

Скрыть нативный, добавить кастомный до/после нативного :)

xShift 04.06.2018 15:49

Nexus, и будете как обезьяна по next prev скакать чтобы selected отметить? Форма то должна работать потом а не просто для красоты.

Nexus 04.06.2018 15:53

xShift, что мешает объявить селекту id (или data-id) и обращаться к нему по id?
Желание спорить отсутствует.

xShift 04.06.2018 16:03

Nexus, у меня тоже нету. Но того требует не только быстрый доступ к свойствам, а хотя бы сущность к примеру БЕМ методологии или OOCSS, который мне в последнее время не нравится изза размножения id. Так удобнее изолировать стили от pre defined и задавать свои политики отображения. Что вы будете горбатого лепить аля замена + оригинал вместо оборачивания?

Nexus 04.06.2018 16:06

xShift, положите ссылку на селект в свойство класса-кастомизатора.

ruslan_mart 04.06.2018 16:35

Ну обычно ссылка на select может храниться где-то в замыкании или в контексте, смотря какая реализация. А обращаться постоянно к элементам через getElemetns*/querySelector, ну или тому подобные - методика для быдлокодеров, аля jQuery головного мозга. Я вообще не признаю innerHTML для перезаписи элементов, а outerHTML уж тем более, это слишком как-то маньячно.

xShift 04.06.2018 18:03

ruslan_mart, а unwrap/wrap это не маньячно?

К примеру:

for(let i of e) {

			let parent = i.parentElement;
		
			while (i.firstChild) {
				parent.insertBefore(i.firstChild, i);
			}

			parent.removeChild(i);
		}


против

i.outerHTML = '<div class="wrapper">'+ i.outerHTML +'</div>';


Я просто как-то элегантнее не видел. Ладно уж parentElement может перестроиться, но closest обязан работать, чтобы не было таких вот while головного мозга.

> querySelector для быдлокодеров

Не правда. Очень крутая фишка, о которой вы возможно не знали это поиск в элементе другого элемента. Это и было круто в jQuery $('.some', '.some-wrapper') - оно отсекало все ненужное.

Но jQuery и сам Sizzle стали не нужна когда появился querySelectorAll.

То же самое:

let element = document.querySelectorAll('#shell');

element[0].querySelectorAll('.somethin-in-shell');


или еще прикольнее:

let element = document.querySelectorAll('#shell');

element[0].querySelectorAll('*'); // вообще все что внутри


И тогда не понятно как вы их умудряетесь получать? везде тыкаете id а потом прямо пишете для div#shell обращение типа shell.dataset.shit-code = 'ololo'; ?

Без этих остается только прямое обращение к объекту в window ...

j0hnik 04.06.2018 18:06

Цитата:

Сообщение от xShift
i.outerHTML = '<div class="wrapper">'+ i.outerHTML +'</div>';

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

xShift 04.06.2018 18:12

j0hnik, оберта в функцию + callback перезапускающий ее(нагрузки там не много - процессор этим не сожрет и память). я вот кстати промисы не люблю.

Alexandroppolus 04.06.2018 18:39

Цитата:

Сообщение от xShift (Сообщение 486626)
ruslan_mart, а unwrap/wrap это не маньячно?

К примеру:

for(let i of e) {

			let parent = i.parentElement;
		
			while (i.firstChild) {
				parent.insertBefore(i.firstChild, i);
			}

			parent.removeChild(i);
		}


против

i.outerHTML = '<div class="wrapper">'+ i.outerHTML +'</div>';


Я просто как-то элегантнее не видел.

вот, способ с сохранением объектов-узлов:
var elem = document.querySelector('#id1');
var range = document.createRange();
range.selectNodeContents(elem);
elem.parentNode.replaceChild(range.extractContents(), elem);


это unwrap, то что у тебя в цикле. Собственно, вытаскиваем весь контент из элемента, и заменяем элемент на него. Два изменения дома - вытаскивание контента и замена.

wrap аналогично.

outerHTML/innerHTML тут нафиг не нужен.

матчасцъ - https://learn.javascript.ru/range-textrange-selection

ruslan_mart 04.06.2018 18:48

xShift, про querySelector я другое имел ввиду.. Что получать один и тот же элемент через querySelector при каком-либо событии - не есть хорошо. Если Вы создаёте какой-то модуль/плагин, то у Вас должна быть инициализация на динамическом уровне, а не на уровне получения элементов "туда-сюда" из вёрстки.

Цитата:

Сообщение от xShift
element[0].querySelectorAll('*');

element[0].getElementsByTagName('*') работает куда быстрее. Но в принципе, в 99.9% случаев никогда нет надобности получать всех потомков всех уровней, так что эта затея не имеет смысла. Обычно есть необходимость получать потомков первого уровня, а делается это элементарно через element.childNodes/children.

Не нужны никакие id при правильном подходе. Если Вы создаёте элемент или дерево элементов, то уже знаете об этом элементе.

this.wrapper = document.createElement('div');

this.wrapper //-> вот она, ссылка на элемент.


Всё что касается innerHTML/outerHTML - это безусловно нужные вещи, но при написании модулей/плагинов использовать это будет не совсем грамотно.

Вернёмся к теме jQuery или querySelector. Вот типичный пример быдлокода:

document.querySelector('#button').onclick = function() {
    document.querySelectorAll('.foo .bar')[0].style.display = 'none';
};




//Не правильно
$('#button').on('click', function() {
   $('.block-1').css('display', 'block');
   $('.block-1').css('color', 'red');
   $('.block-2').html('Bla');
});


//Правильно
var block_1 = $('.block-1'),
   block_2 = $('.block-2');

$('#button').on('click', function() {
   block_1.css({display: 'block', color: 'red'});
   block_2.html('Bla');
});

ruslan_mart 04.06.2018 18:56

Цитата:

Сообщение от xShift
let element = document.querySelectorAll('#shell');
 
element[0].querySelectorAll('.somethin-in-shell');

Даже если рассматривать Ваш пример, то тут очень много противоречий.

1. id уникален, он должен быть только один на странице. Следовательно, запись document.querySelectorAll('#shell') не имеет смысла, так как мы хотим получить один единственный элемент, а не коллекцию. Получается, что лучше тогда использовать document.querySelector (без "All"), но опять же, куда быстрее и правильней будет заюзать document.getElementById.

2. Нет смысла в два шага искать элементы с определённым классом внутри элемента с определённым id. Можно сразу:
document.querySelectorAll('#shell .somethin-in-shell')

Но это если конечно Вам не нужны оба элемента.

xShift 04.06.2018 19:37

Alexandroppolus, в этом году не смотрел, но в прошлом году вот replace child имел своеобразные глюки в сафари. А про range не знал. Возможно полезно, но я даже не смотря на казалось бы более правильный подход пользоваться этим не буду никогда. Какая-то лишняя ненужна абстракция. От того еще один querySelector появится никто не умрет.

Я щас проще к Safari загляну и предложу исправить данный недочет с обновлением узлов в closest. После этого все автоматически справится в Chrome, Yandex браузере, куче поделок из хромиума и возможно даже повлияет на FF потому что Safari разработчики первоисходных стандартов. Они это быстрее решают. Я год ждал ответа от MS по поводу бага в Edge. Они только недавно отписались и приняли статус issue.

Это конечно хорошо, но не правильно(если есть недоработки их не надо игнорировать путем выворачивания вселенной на изнанку). Наелись с IE - хватит. Щас еще андроида придется за это же убивать, но это уже другая история.

Даже если рассматривать Ваш пример, то тут очень много противоречий.

1. id уникален, он должен быть только один на странице. Следовательно, запись document.querySelectorAll('#shell') не имеет смысла, так как мы хотим получить один единственный элемент, а не коллекцию. Получается, что лучше тогда использовать document.querySelector (без "All"), но опять же, куда быстрее и правильней будет заюзать document.getElementById.

2. Нет смысла в два шага искать элементы с определённым классом внутри элемента с определённым id. Можно сразу:
document.querySelectorAll('#shell .somethin-in-shell')

Но это если конечно Вам не нужны оба элемента.


ruslan_mart, All у меня везде используется потому что я себе библиотеку написал которая сразу коллекциями работает, а не по одному элементу. Мне на эти копейки пофиг экномии.

By id. by class, tag name - это устаревшее барахло по моему. Опять же экономить на спичках, когда можно прикуривать шикарной Zippo.


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