Хорошо, прости меня за то, как это велико. Я подумал, что это очень интересный вопрос, но, поиграв с ним, я быстро понял, что innerHTML и его аналог довольно ненадежны в отношении сохранения пробелов, комментариев и т. Д. Имея это в виду, я вернулся к созданию полной копии источник, чтобы я мог быть абсолютно уверен, что я получил полный источник. Затем я использовал jquery и несколько (относительно небольших) регулярных выражений, чтобы найти местоположение каждого узла. Кажется, это работает хорошо, хотя я уверен, что пропустил некоторые крайние случаи. И да, да, регулярные выражения и две проблемы, бла-бла-бла.
Редактировать: В качестве упражнения по созданию плагинов jquery я изменил свой код так, чтобы он функционировал достаточно хорошо, в качестве отдельного плагина с примером , аналогичным HTML, найденный ниже (который я оставлю здесь для потомков). Я попытался сделать код немного более устойчивым (например, теперь обрабатывать теги внутри строк в кавычках, например onclick), но самая большая остающаяся ошибка - это то, что он не может объяснить любые изменения на странице, такие как добавление элементов. Возможно, мне понадобится использовать iframe вместо вызова ajax для обработки этого случая.
<code><html>
<head id="node0">
<!-- first comment -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style id="node1">
/* div { border: 1px solid black; } */
pre { border: 1px solid black; }
</style>
<!-- second comment -->
<script>
$(function() {
// fetch and display source
var source;
$.ajax({
url: location.href,
type: 'get',
dataType: 'text',
success: function(data) {
source = data;
var lines = data.split(/\r?\n/);
var html = $.map(lines, function(line, i) {
return ['<span id="line_number_', i, '"><strong>', i, ':</strong> ', line.replace(/</g, '<').replace(/>/g, '>'), '</span>'].join('');
}).join('\n');
// now sanitize the raw html so you don't get false hits in code or comments
var inside = false;
var tag = '';
var closing = {
xmp: '<\\/\\s*xmp\\s*>',
script: '<\\/\\s*script\\s*>',
'!--': '-->'
};
var clean_source = $.map(lines, function(line) {
if (inside && line.match(closing[tag])) {
var re = new RegExp('.*(' + closing[tag] + ')', 'i');
line = line.replace(re, "$1");
inside = false;
} else if (inside) {
line = '';
}
if (line.match(/<(script|!--)/)) {
tag = RegExp.$1;
line = line.replace(/<(script|xmp|!--)[^>]*.*(<(\/(script|xmp)|--)?>)/i, "<$1>$2");
var re = new RegExp(closing[tag], 'i');
inside = ! (re).test(line);
}
return line;
});
// nodes we're looking for
var nodes = $.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(num) { return $('#node' + num) });
// now find each desired node in both the DOM and the source
var line_numbers = $.map(nodes, function(node) {
var tag = node.attr('tagName');
var tags = $(tag);
var index = tags.index(node) + 1;
var count = 0;
for (var i = 0; i < clean_source.length; i++) {
var re = new RegExp('<' + tag, 'gi');
var matches = clean_source[i].match(re);
if (matches && matches.length) {
count += matches.length;
if (count >= index) {
console.debug(node, tag, index, count, i);
return i;
}
}
}
return count;
});
// saved till end to avoid affecting source html
$('#source_pretty').html(html);
$('#source_raw').text(source);
$('#source_clean').text(clean_source.join('\n'));
$.each(line_numbers, function() { $('#line_number_' + this).css('background-color', 'orange'); });
},
});
var false_matches = [
"<div>",
"<div>",
"</div>",
"</div>"
].join('');
});
</script>
</head>
<!-- third comment -->
<body id="node2">
<div>
<pre id="source_pretty">
// <xmp> is deprecated, you should put it in <code> instead
<! - четвертый комментарий ->
<! - пятый комментарий ->