Я отображаю некоторый простой стилизованный текст как HTML в UIWebView на iPhone. Это - в основном ряд абзацев со случайной сильной или подчеркнутой фразой. Во времени выполнения я должен применить стили к диапазонам текста.
Существует несколько подобных сценариев, один из которых выделяет результаты поиска. Если бы пользователь искал "что-то", что я хотел бы изменить цвет фона позади случаев слова, то позже восстанавливают исходный фон.
Действительно ли возможно применить стили к диапазонам текста с помощью JavaScript? Ключевая роль этого является также способностью сбросить стили.
Кажется, существует два вероятных пути для следования. Можно было бы изменить некоторый HTML в Objective C и передал бы его через JavaScript как новый innerHTML некоторого контейнера. Другой должен был бы использовать JavaScript для прямого управления узлами DOM.
Я мог управлять HTML, но это звучит утомительным в Objective C, таким образом, я управлял бы DOM, если это - разумный подход. Я не настолько знаком с JavaScript и DOM, таким образом, я не знаю, является ли это разумный подход.
Я записал некоторые стандартные программы для перевода между текстовыми диапазонами и диапазонами узла со смещениями. Таким образом, если я запускаю с текстового диапазона 100-200, и это запускается в одном абзаце и концах в одной трети, я могу получить текстовые узлы и смещения в узлах, которые представляют данный текстовый диапазон. Мне просто нужен способ разделить текстовый узел при смещении в тексте. В настоящее время я просто применяю стили к абзацам, содержащим текстовый диапазон.
Несколько примечаний:
Спасибо за любые предложения.
Я думаю, вы многого просите, чтобы получить для этого полное решение, но это показалось интересным, поэтому я реализовал его. Следующее работает в последних версиях браузеров WebKit, включая Safari на iPhone под управлением OS 3.0. Он использует нестандартный, но удобный метод intersectsNode
из Range
, который существует в WebKit, но был удален из Firefox в версии 3.0, поэтому он не работает в последних версиях Firefox, но может сделать это тривиально.
Следующее будет окружать каждый выбранный текстовый узел элементом
с классом «someclass», а также уникальным классом, позволяющим легко отменить. applyClassToSelection
возвращает этот уникальный класс; передайте этот класс в removeSpansWithClass
, чтобы удалить промежутки.
ОБНОВЛЕНИЕ: Исправлена проблема, когда выделение полностью содержится в одном текстовом узле.
ОБНОВЛЕНИЕ 2: Теперь протестировано и работает в iPhone под управлением OS 3.0.
ОБНОВЛЕНИЕ 3: Добавлена функция rangeIntersectsNode
для добавления поддержки Firefox 3.0 и новее. Этот код теперь должен работать в Firefox 1.0+, Safari 3.1+, Google Chrome, Opera 9.6+ и, возможно, в других (пока не проверено). Он вообще не работает в Internet Explorer и выдает ошибки в этом браузере. Я планирую вскоре поработать над версией IE.
<script type="text/javascript">
var nextId = 0;
var rangeIntersectsNode = (typeof window.Range != "undefined"
&& Range.prototype.intersectsNode) ?
function(range, node) {
return range.intersectsNode(node);
} :
function(range, node) {
var nodeRange = node.ownerDocument.createRange();
try {
nodeRange.selectNode(node);
} catch (e) {
nodeRange.selectNodeContents(node);
}
return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
};
function applyClassToSelection(cssClass) {
var uniqueCssClass = "selection_" + (++nextId);
var sel = window.getSelection();
if (sel.rangeCount < 1) {
return;
}
var range = sel.getRangeAt(0);
var startNode = range.startContainer, endNode = range.endContainer;
// Split the start and end container text nodes, if necessary
if (endNode.nodeType == 3) {
endNode.splitText(range.endOffset);
range.setEnd(endNode, endNode.length);
}
if (startNode.nodeType == 3) {
startNode = startNode.splitText(range.startOffset);
range.setStart(startNode, 0);
}
// Create an array of all the text nodes in the selection
// using a TreeWalker
var containerElement = range.commonAncestorContainer;
if (containerElement.nodeType != 1) {
containerElement = containerElement.parentNode;
}
var treeWalker = document.createTreeWalker(
containerElement,
NodeFilter.SHOW_TEXT,
// Note that Range.intersectsNode is non-standard but
// implemented in WebKit
function(node) {
return rangeIntersectsNode(range, node) ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);
var selectedTextNodes = [];
while (treeWalker.nextNode()) {
selectedTextNodes.push(treeWalker.currentNode);
}
var textNode, span;
// Place each text node within range inside a <span>
// element with the desired class
for (var i = 0, len = selectedTextNodes.length; i < len; ++i) {
textNode = selectedTextNodes[i];
span = document.createElement("span");
span.className = cssClass + " " + uniqueCssClass;
textNode.parentNode.insertBefore(span, textNode);
span.appendChild(textNode);
}
return uniqueCssClass;
}
function removeSpansWithClass(cssClass) {
var spans = document.body.getElementsByClassName(cssClass),
span, parentNode;
// Convert spans to an array to prevent live updating of
// the list as we remove the spans
spans = Array.prototype.slice.call(spans, 0);
for (var i = 0, len = spans.length; i < len; ++i) {
span = spans[i];
parentNode = span.parentNode;
parentNode.insertBefore(span.firstChild, span);
parentNode.removeChild(span);
// Glue any adjacent text nodes back together
parentNode.normalize();
}
}
var c;
</script>
<input type="button" onclick="c = applyClassToSelection('someclass')"
value="Add class">
<input type="button" onclick="removeSpansWithClass(c)"
value="Remove class">