Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 17.01.2014, 18:22
Аватар для FINoM
Новичок
Отправить личное сообщение для FINoM Посмотреть профиль Найти все сообщения от FINoM
 
Регистрация: 05.09.2010
Сообщений: 2,298

Парсинг HTML -> DOM в нормальных браузерах (таки проблема)
Господа, пишу библиотеку по работе с DOM (для новых браузеров) и столкнулся с проблемой парсинга html. Конечная цель - сделать максимально компактно (< 1K). Это очередной мой опен соурц проект, о котором будет рассказано на Хабре и здесь.

К сожалению, я не могу найти простого решения парсинга HTML и представления его в виде DOM. Что первое приходит в голову? Создание элемента (дива) и вставка в него html.

div = document.createElement( 'div' );
div.innerHTML = '<p>a<p>b';
alert( div.innerHTML );
myDomNodes = div.children;


Проблема возникает, когда нам нужно распарсить элемент таблицы.
div = document.createElement( 'div' );
div.innerHTML = '<tr>a</tr><tr>b</tr>';
alert( div.innerHTML );

Понятно, почему так происходит: tr, td, tbody, thead, tfoot... не могут находиться внутри элементов, не являющихся их родителями по спецификации.

Другой вариант: использовать DOMParser
var doc = new DOMParser().parseFromString( '<tr>a</tr><tr>b</tr>', "text/html");
alert( doc.body.innerHTML );


Та же проблема.

Пробуем поменять контент-тайп на xml (потмо можно "усыновить" ноды методом document.adoptNode):
var doc = new DOMParser().parseFromString( '<tr>a</tr><tr>b</tr>', "application/xml");
console.log( doc );


Не ок. Метод будет парсить любой код выше (<tr>a</tr><tr>b</tr>, <p>a<p>b) с ошибками (не закрытый тег, который по стандарту html и не требуется закрывать, отсутствия рут элемента....)
Значит и этот метод нам не подходит.

А что по поводу DocumentFragment? Ничего. У него нет свойства innerHTML. Печаль.

Как это делается в библиотеках?
http://habrahabr.ru/post/164533/

Но предварительно jQuery пытается найти, не нужно ли обрамить наш код как-то дополнительно. Берется самый первый найденный в коде тег и ищется в служебном объекте wrapMap.

В Библиотеках это делается через, простите, жопу.

На дворе 2014 год и нет ни одного способа распарсить HTML в последнем хроме и файерфоксе? Не верю.
__________________
"Matreshka is fucking awesome" © чувак с Reddit
Matreshka.js - Три возможности
Ответить с цитированием
  #2 (permalink)  
Старый 17.01.2014, 18:45
Аватар для danik.js
Профессор
Отправить личное сообщение для danik.js Посмотреть профиль Найти все сообщения от danik.js
 
Регистрация: 11.09.2010
Сообщений: 8,804

Сообщение от FINoM
На дворе 2014 год и нет ни одного способа распарсить HTML в последнем хроме и файерфоксе?
Если ты хочешь парсить фрагмент, то ты обязан указать контекст фрагмента. Соответственно создавать надо не div, а tbody

Сообщение от FINoM
А что по поводу DocumentFragment? Ничего. У него нет свойства innerHTML. Печаль.
Это по той же самой причине - парсеру нужно знать контекст для корректного разбора HTML. А у фрагмента нет контекста.

Сообщение от FINoM
В Библиотеках это делается через, простите, жопу.
Зато от юзера не требуется указание контекста. То есть это элемент "умного поведения" - такие вещи обычно и делаются "через жопу"

А вот то что DOMParser не умеет парсить фрагменты - это недочет, как мне кажется.
__________________
В личку только с интересными предложениями
Ответить с цитированием
  #3 (permalink)  
Старый 18.01.2014, 02:21
Профессор
Отправить личное сообщение для kostyanet Посмотреть профиль Найти все сообщения от kostyanet
 
Регистрация: 23.10.2010
Сообщений: 2,718

Все-таки хотел узнать за каким парсить хтмл вообще? Ну я понимаю читеркод, когда исходник не твой и надо ковырять чужой моск. Но если он твой, то какой может быть задача пихать еду через задницу?
Ответить с цитированием
  #4 (permalink)  
Старый 18.01.2014, 02:35
Аватар для FINoM
Новичок
Отправить личное сообщение для FINoM Посмотреть профиль Найти все сообщения от FINoM
 
Регистрация: 05.09.2010
Сообщений: 2,298

Сообщение от danik.js
парсеру нужно знать контекст для корректного разбора HTML
В этом и косяк. Когда я юзаю createElement, методу совершенно пофиг на контекст создаваемого элемента. Но, блин, обычной, очевидно нужной, функции обычного парсинга в ДОМе нет.
__________________
"Matreshka is fucking awesome" © чувак с Reddit
Matreshka.js - Три возможности
Ответить с цитированием
  #5 (permalink)  
Старый 18.01.2014, 07:26
Аватар для danik.js
Профессор
Отправить личное сообщение для danik.js Посмотреть профиль Найти все сообщения от danik.js
 
Регистрация: 11.09.2010
Сообщений: 8,804

Сообщение от FINoM
Но, блин, обычной, очевидно нужной, функции обычного парсинга в ДОМе нет.
Специфика HTML синтаксиса такова, что парсеру нужно знать контекст. От контекста зависит поведение парсера. Понимаешь?

Это XML можно разобрать в отрыве от контекста. С HTML это не прокатит.
Например если HTML-парсер встретит тег <td> разбирая содержимое div'а, то он проигнорирует этот тег и вставит только его содержимое. А если это произойдет в контексте <table>, <tbody> или <tr> то он его не проигнорирует, да к тому же создаст недостающие части таблицы (tbody, tr). То есть HTML парсер имеет кучу состояний, зависящих от контекста.
Теперь понимаешь почему у DocumentFragment нет innerHTML?
Ах да, в разных контекстах не только разное поведение при встрече тегов, но даже в некоторых контекстах теги могут вобще распознаваться как текст. Это касается контекстов <title>, <noscript>, <plaintext>, <textarea> и тд. Внутри <script> вообще особое поведение - в нем распознается только закрывающий тег </script>

Матчасть: http://www.whatwg.org/specs/web-apps...sing-algorithm
__________________
В личку только с интересными предложениями
Ответить с цитированием
  #6 (permalink)  
Старый 18.01.2014, 07:28
Аватар для danik.js
Профессор
Отправить личное сообщение для danik.js Посмотреть профиль Найти все сообщения от danik.js
 
Регистрация: 11.09.2010
Сообщений: 8,804

Сообщение от FINoM
Когда я юзаю createElement, методу совершенно пофиг на контекст создаваемого элемента
Для создания элемента парсер не требуется. Контекст нужен только для парсера. А парсер нужен только для разбора HTML-кода.
__________________
В личку только с интересными предложениями
Ответить с цитированием
  #7 (permalink)  
Старый 18.01.2014, 13:38
Аватар для FINoM
Новичок
Отправить личное сообщение для FINoM Посмотреть профиль Найти все сообщения от FINoM
 
Регистрация: 05.09.2010
Сообщений: 2,298

Сообщение от danik.js
Теперь понимаешь почему у DocumentFragment нет innerHTML?
Нет, не понимаю. Логично предположить, что для DocumentFragment можно реализовать парсинг HTML с "любым" контекстом. Понятное дело, что если мы парсим <div><td></div>, то этот гипотетический парсер вернул бы только <div>, так как в диве не может быть <td> но если <td> на верхнем уровне (<td><div></div>), зачем ему вообще контекст? Почему персер не может, условно, сделать document.createElement( 'элемент верхнего уровня' );, а затем уже, для элементов внутри, исправлять ошибки.

Ваша аргументация мне понятна. Описание поведения в таких случаях описано в спецификации. И моё недовольство касается не реализации чего-либо в браузерах, а документального описания реализации в спецификации.
Сообщение от danik.js
Для создания элемента парсер не требуется.
Почему? Они бы могли .createElement сделать тоже зависимым от контекста: document.createElement( 'tbody', document.querySelector( '#tbody' ) ). Это тоже было бы верно. Ответ на этот вопрос у меня один: они посчитали это неудобным и решили реализовать создание элементов в отрыве от контекста. Спасибо им. Почему они не посчитали удобным реализовать парсер, мне не понятно.

Ну да ладно, ответа на мой вопрос нет и, судя по всему не будет. А спорить мы можем долго.
__________________
"Matreshka is fucking awesome" © чувак с Reddit
Matreshka.js - Три возможности
Ответить с цитированием
  #8 (permalink)  
Старый 18.01.2014, 14:19
Аватар для danik.js
Профессор
Отправить личное сообщение для danik.js Посмотреть профиль Найти все сообщения от danik.js
 
Регистрация: 11.09.2010
Сообщений: 8,804

Сообщение от FINoM
Почему?
Что за вопрос. Ты считаешь что при вызове метода createElelement() происходит вызов парсера? А че парсить то?

Сообщение от FINoM
Они бы могли .createElement сделать тоже зависимым от контекста
А какой в этом смысл?

Сообщение от danik.js
Почему персер не может, условно, сделать document.createElement( 'элемент верхнего уровня' );, а затем уже, для элементов внутри, исправлять ошибки
Потому что это мало требуемая сомнительная фишка (что это за ситуация, когда ты не знаешь в какой контекст будут вставлены распарсенные DOM-элементы? ), которая бы усложнила и без того запутанную логику парсера.
И наверное если подумать, то всплывут ситуации, где нельзя "догадаться" о контексте.
__________________
В личку только с интересными предложениями
Ответить с цитированием
  #9 (permalink)  
Старый 18.01.2014, 14:43
Аватар для FINoM
Новичок
Отправить личное сообщение для FINoM Посмотреть профиль Найти все сообщения от FINoM
 
Регистрация: 05.09.2010
Сообщений: 2,298

danik.js, вы, как и в предыдущих сообщениях защищаете спецификацию. Моя точка зрения: спецификаторы должны были до 2014 (!) года предусмотреть безконтекстный парсер.

Повторюсь.
Сообщение от FINoM
Почему персер не может, условно, сделать document.createElement( 'элемент верхнего уровня' );, а затем уже, для элементов внутри, исправлять ошибки.
Косяк спецификации, на мой взгляд.
Сообщение от danik.js
Потому что это мало требуемая сомнительная фишка (что это за ситуация, когда ты не знаешь в какой контекст будут вставлены распарсенные DOM-элементы? ),
Сообщение от danik.js
И наверное если подумать, то всплывут ситуации, где нельзя "догадаться" о контексте.
Угу. Например, задача из первого поста.
__________________
"Matreshka is fucking awesome" © чувак с Reddit
Matreshka.js - Три возможности
Ответить с цитированием
  #10 (permalink)  
Старый 19.01.2014, 17:38
Аватар для FINoM
Новичок
Отправить личное сообщение для FINoM Посмотреть профиль Найти все сообщения от FINoM
 
Регистрация: 05.09.2010
Сообщений: 2,298

Написал простейшее решение. Проверяется только первый тег в строке, для остальных контекст остается тем же, что и для первого.
window.parseHTML = function( html ) {
	var node = document.createElement( 'div' ),
		// wrapMap is taken from jQuery
		wrapMap = {
				option: [ 1, "<select multiple='multiple'>", "</select>" ],
				legend: [ 1, "<fieldset>", "</fieldset>" ],
				thead: [ 1, "<table>", "</table>" ],
				tr: [ 2, "<table><tbody>", "</tbody></table>" ],
				td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
				col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
				area: [ 1, "<map>", "</map>" ],
				_default: [ 0, "", "" ]
		},
		wrapper,
		i;
	
	wrapMap.optgroup = wrapMap.option;
	wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
	wrapMap.th = wrapMap.td;
	
	wrapper = wrapMap[ /<([\w:]+)/.exec( html )[ 1 ] ] || wrapMap._default;
	
	node.innerHTML = wrapper[ 1 ] + html + wrapper[ 2 ];
	
	i = wrapper[ 0 ];

	while( i-- ) {
		node = node.firstElementChild;
	}
	
	return node.children;
};
__________________
"Matreshka is fucking awesome" © чувак с Reddit
Matreshka.js - Три возможности
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Проблема метода indexOf() в разных браузерах! Rodger1956629 Javascript под браузер 7 31.07.2013 02:54
Как в браузерах реализуются функции DOM (например createElement) iamme Общие вопросы Javascript 7 02.09.2011 20:26
Вопрос о циклических ссылках (JavaScript -> Dom -> JavaScipt) BlueIce Events/DOM/Window 10 17.02.2010 21:58