2013-07-17 3 views
1

Я использую плагин jQuery Xpath для просмотра иерархии HTML-документа. Я не использую селектора по той причине, что мне нужно обрабатывать информацию о серверной структуре, которая сообщает мне Xpath конкретному элементу.Xpath по HTML: разница между источником HTML и DOM-моделью

Теперь я заметил, что DOM не обязательно представляет иерархию HTML-документа и нашел решение этой проблемы здесь: Why does firebug add <tbody> to <table>?. Это означает, что если мой HTML документ делает, например, содержать следующий код:

<table> 
    <tr> 
    <td>Hello</td> 
    </tr> 
</table> 

Мой DOM будет представлять последний так:

<table> 
    <tbody> 
    <tr> 
     <td>Hello</td> 
    </tr> 
    </tbody> 
</table> 

Мой Xpath запрос

jQuery(document).xpath('//table[1]/tr[1]/td[1]') 

делает поэтому больше не дают результата.

Есть ли способ избежать синтетических элементов представления DOM? Или способ настроить Xpath так, чтобы он включал синтетические элементы? Спасибо за любую помощь.

+0

Либо инфраструктура, предоставляющая вам XPaths, является ошибкой, либо у вас есть несоответствие между документом, который рассматривается как XML/XHTML на сервере, и как HTML в браузере. – Alohci

+0

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

ответ

1

Ну, с помощью JQuery Я сфабриковал этот альтернативный XPath-парсер, который работает для моего сценария использования. Парсер пытается остановиться на XPath, указанном моим входом, но если модель DOM добавляет новый тег в середине пути, где оставшаяся часть пути обернута в этот один элемент, парсер распознает это дополнение и включает в себя этот отдельные элементы в путь. Разумеется, это не будет работать для всех и каждого сценария, но это работает для меня. Может быть, это решение помочь кому-либо еще, по крайней мере, после некоторого расширения:

var SloppyXPathParser = (function() { 

    function childExists($cursor, element) { 
     assertSelection($cursor); 
     var $movedCursor = $cursor.children(element.name); 
     if ($movedCursor.size() > element.index) { 
      return jQuery($movedCursor.get(element.index)); 
     } else if ($cursor.children().size() == 1) { 
      return childExists(jQuery($cursor.children().get(0)), element); 
     } else { 
      throw 'Cannot browse to \'' + element.name + '\' at index ' + element.index + '\''; 
     } 
    } 

    function assertSelection($cursor) { 
     if (!($cursor instanceof jQuery) || $cursor.size() != 1) { 
      throw 'Selection is invalid: ' + $cursor.size(); 
     } 
    } 

    function parsePath(rawPath) { 
     var nodes = rawPath.split('/'); 
     var regex = new RegExp('([a-zA-Z]+)\\[([0-9]+)\\]'); 
     var elements = []; 
     var index = 0; 
     jQuery(nodes).each(function (key, element) { 
      if (element.length == 0) { 
       return true; 
      } 
      if (!regex.test(element)) { 
       throw 'Path element does not match regex: ' + element; 
      } 
      var matched = regex.exec(element); 
      elements[index++] = { name: matched[1], index: matched[2] }; 
     }); 
     return elements; 
    } 

    function findElement(input) { 

     var elements = parsePath(input); 
     var $cursor = jQuery(document); 
     jQuery(elements).each(function (key, element) { 
      $cursor = childExists($cursor, element); 
     }); 

     try { 
      assertSelection($cursor); 
     } catch (cause) { 
      console.log('Exception: ' + cause); 
      return false; 
     } 

     return $cursor.get(0); 
    } 

    return { 
     find: function (input) { 
      return findElement(input); 
     } 
    } 
})(); 

var input = '/html[0]/body[0]/table[0]/tr[1]/td[1]'; 
SloppyXPathParser.find(input); 

с исходной HTML бытия:

<html> 
    <body> 
    <table> 
     <tr> 
     <td>wrong</td> 
     <td>wrong</td> 
     </tr> 
     <tr> 
     <td>wrong</td> 
     <td>right</td> 
     </tr> 
    </table> 
    </body> 
</html> 

Вы можете проверить, например, Firebug, который браузер добавляет в DOM элемент tbody. Парсер распознает это и пропустит запись.

0

Если у вас нет вложенных таблиц, jQuery(document).xpath('//table[1]//tr[1]/td[1]') должен работать в обоих случаях.

В более общем случае, вы можете адаптировать от ответа на How do you select child-or-self (children + self)

В XPath 1.0, это было бы перевести на jQuery(document).xpath('(//table|//table/tbody)/tr[1]/td[1]') или даже в более общем jQuery(document).xpath('(//table|//table/node())/tr[1]/td[1] «)

+0

Это будет работать для этого конкретного случая, но не для общего ввода. –

+0

Отредактировано с помощью решения, адаптированного из http://stackoverflow.com/questions/4311470/how-do-you-select-child-or-self-children-self –

0

Просто превратить один / в двойной для вашего tr:

//table[1]/tr[1]/td[1] ->//table[1]//tr[1]/td[1]

Это будет соответствовать строке таблицы на любой глубине ниже исходной таблицы тега, так что вы можете добавить столько, сколько <tbody> теги, как вам нравится.

+0

Ну что, если документ содержит вложенную таблицу? Это добавит неоднозначность. –

+0

Предполагая, что вы больше не будете соответствовать только первым «» и «», то да. Вы можете подумать о добавлении атрибута класса, чтобы определить, на каком уровне находятся теги '', но я думаю, что это было бы немного грязным взломом. – Tro

Смежные вопросы