apply style to range of text with javascript in uiwebview

后端 未结 1 1720
别那么骄傲
别那么骄傲 2020-12-01 00:45

I am displaying some simple styled text as html in a UIWebView on iPhone. It is basically a series of paragraphs with the occasional strong or emphasized phrase. At runtim

相关标签:
1条回答
  • 2020-12-01 00:53

    I think you're asking a lot to get a complete solution for this, but it seemed interesting so I've implemented it. The following works in recent WebKit browsers, including Safari on iPhone running OS 3.0. It uses the non-standard but convenient intersectsNode method of Range, which exists in WebKit but was removed from Firefox in 3.0, so it doesn't work in recent versions of Firefox but could be made to do so trivially.

    The following will surround each selected text node with a <span> element with a class of "someclass" and also a unique class to allow easy undoing. applyClassToSelection returns this unique class; pass this class into removeSpansWithClass to remove the spans.

    UPDATE: Fixed problem when selection is entirely contained within a single text node

    UPDATE 2: Now tested and works in iPhone running OS 3.0.

    UPDATE 3: Added rangeIntersectsNode function to add support for Firefox 3.0 and later. This code should now work in Firefox 1.0+, Safari 3.1+, Google Chrome, Opera 9.6+ and possibly others (untested so far). It does not work at all in Internet Explorer and will give errors in that browser. I plan to work on an IE version soon.

    <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">
    
    0 讨论(0)
提交回复
热议问题