Парсинг 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 в последнем хроме и файерфоксе? Не верю. |
Цитата:
Цитата:
Цитата:
А вот то что DOMParser не умеет парсить фрагменты - это недочет, как мне кажется. |
Все-таки хотел узнать за каким парсить хтмл вообще? Ну я понимаю читеркод, когда исходник не твой и надо ковырять чужой моск. Но если он твой, то какой может быть задача пихать еду через задницу?
|
Цитата:
|
Цитата:
Это 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 |
Цитата:
|
Цитата:
Ваша аргументация мне понятна. Описание поведения в таких случаях описано в спецификации. И моё недовольство касается не реализации чего-либо в браузерах, а документального описания реализации в спецификации. Цитата:
Ну да ладно, ответа на мой вопрос нет и, судя по всему не будет. А спорить мы можем долго. |
Цитата:
Цитата:
Цитата:
И наверное если подумать, то всплывут ситуации, где нельзя "догадаться" о контексте. |
danik.js, вы, как и в предыдущих сообщениях защищаете спецификацию. Моя точка зрения: спецификаторы должны были до 2014 (!) года предусмотреть безконтекстный парсер.
Повторюсь. Цитата:
Цитата:
Цитата:
|
Написал простейшее решение. Проверяется только первый тег в строке, для остальных контекст остается тем же, что и для первого.
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; }; |
Часовой пояс GMT +3, время: 01:42. |