【问题标题】:apply style to range of text with javascript in uiwebview在uiwebview中使用javascript将样式应用于文本范围
【发布时间】:2011-02-22 15:30:23
【问题描述】:

我在 iPhone 上的 UIWebView 中将一些简单样式的文本显示为 html。它基本上是一系列段落,偶尔会出现强烈或强调的短语。在运行时,我需要将样式应用于文本范围。

有一些类似的场景,其中之一是突出显示搜索结果。如果用户搜索过“某事”,我想更改单词出现后的背景颜色,然后恢复原始背景。

是否可以使用 javascript 将样式应用于文本范围?其中一个关键部分是能够取消设置样式。

似乎有两条可能的路径可供选择。一种是在 Objective-C 中修改一些 html 并通过 javascript 将其作为某个容器的新 innerHTML 传递。另一种是使用javascript直接操作DOM节点。

我可以操作 html,但在 Objective-C 中这听起来很乏味,所以如果这是一种合理的方法,我宁愿操作 DOM。我对javascript和DOM不太熟悉,所以我不知道这是否合理。

我编写了一些例程来在文本范围和带有偏移的节点范围之间进行转换。因此,如果我从文本范围 100-200 开始,并且从一个段落开始并以第三个段落结束,我可以获得代表给定文本范围的节点内的文本节点和偏移量。我只需要一种在文本偏移处拆分文本节点的方法。目前我只是将样式应用于包含文本范围的段落。

几点说明:

  • 请直接使用 javascript,不要使用 jquery 等外部框架。
  • 无需将更改写入磁盘。
  • 更改应该是可撤消的或至少是可移除的。
  • 要应用的样式已存在于 css 文件中。
  • 它需要在 iPhone 3.0 及更高版本中运行。
  • 应用随附所有源文件。
  • 请详细一点。

感谢您的任何建议。

【问题讨论】:

    标签: javascript iphone html css uiwebview


    【解决方案1】:

    我认为您要求很多才能获得完整的解决方案,但这似乎很有趣,所以我已经实现了它。以下适用于最近的 WebKit 浏览器,包括运行 OS 3.0 的 iPhone 上的 Safari。它使用Range 的非标准但方便的intersectsNode 方法,该方法存在于WebKit 中,但在3.0 中已从Firefox 中删除,因此它在最新版本的Firefox 中不起作用,但可以轻松做到这一点。

    以下内容将使用<span> 元素围绕每个选定的文本节点,该元素具有“someclass”类以及一个允许轻松撤消的唯一类。 applyClassToSelection 返回这个唯一的类;将此类传递给removeSpansWithClass 以删除跨度。

    更新:修复了选择完全包含在单个文本节点中时的问题

    更新 2:现已在运行 OS 3.0 的 iPhone 上测试并运行。

    更新 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">
    

    【讨论】:

    • 谢谢。这超出了我的预期。
    • 也许把这个放在github上,这样我们至少可以关注。
    • 我正在开发一个跨浏览器范围/选择库,其中将包含此改进版本。进展不大,但我创建了一个 Google Code 项目:code.google.com/p/rangy
    • @Mark、@user306253、@Glasswing、@jozsef:我已经用一个可以在所有浏览器中执行此操作的模块更新了我的库,包括 IE:code.google.com/p/rangy
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多