Any alternative to using execCommand for Highlighting HTML in iOS ebook reader

可紊 提交于 2019-12-21 02:59:07

问题


I'm developing a book reader for iOS using a UIWebView. At the moment I'm working with some basic HTML files but will eventually work with ePubs. I am looking for a suitable way of styling ranges of text. My ranges are a little bit special in that they usually encompass three ranges - a keyrange and a range immediately before and range immediately after. The keyrange may span multiple nodes and may start or end for example within a selection of bold text etc. The styling should not be written to file.

At the moment I have a working solution as follows:

document.designMode = "on";

// Color the first section
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range1);

if (!selection.isCollapsed){ 
document.execCommand("foreColor", false, color1);    
}

// Color the middle section
selection.removeAllRanges();
selection.addRange(range2);

if (!selection.isCollapsed){   
document.execCommand("backColor", false, color2);
document.execCommand("foreColor", false, color3);
}

// Color the last section
selection.removeAllRanges();
selection.addRange(range3);

if (!selection.isCollapsed){ 
document.execCommand("foreColor", false, color1);
}

document.designMode = "off";
selection.removeAllRanges();

This works fine but is noticeably slow (on an iPad2), even if I modify it to highlight a single range in a short HTML document. There is always a very noticeable delay before the text is stylised. Looking at ebook readers on iPad such as kindle or iBooks there is no discernible delay. How might they implement their highlighting function? Might they be reading the geographical location on the selected text and applying some kind of overlay?

I have searched for a better solution than I'm already using but with no luck so if anyone has an idea I would be very grateful.

Thank you!


回答1:


I have made an alternative method to above which appears to be much faster. It is mainly based on mikeB's excellent code given in this question.

How to get nodes lying inside a range with javascript?

  1. I first split the start and end nodes creating new text nodes with only the text which falls with the given range. I adjust the range for the new nodes.

  2. I use a slightly modified version of the code at the above link to return an array of text nodes contained within the range.

  3. I loop through the array and put spans with styles around each text node.

As far as I've tested the code works well. When applying styles to a number of ranges simultaneously there is still a delay but its much better than before. I'd be interested to know if anyone has any improvements.

My code:

function styleRange(range, style) // the style is a string of css styles. eg."background-color:darkblue; color:blue;"
{    
    // Get the start and end nodes and split them to give new start and end nodes with only text that falls inside the range.
    var startNode = range.startContainer.splitText(range.startOffset);
    var endNode = range.endContainer.splitText(range.endOffset).previousSibling;

    // Adjust the range to contain the new start and end nodes
    // The offsets are not really important anymore but might as well set them correctly
    range.setStart(startNode,0);
    range.setEnd(endNode,endNode.length);

    // Get an array of all text nodes within the range
    var nodes = getNodesInRange(range);

    // Place span tags with style around each textnode
    for (i = 0; i < nodes.length; i++)
    {
        var span = document.createElement('span');
        span.setAttribute("style", style);
        span.appendChild( document.createTextNode(nodes[i].nodeValue));
        nodes[i].parentNode.replaceChild( span, nodes[i] );
    }
}

Modified code from mikeB's code:

function getNodesInRange(range)
{
    var start = range.startContainer;
    var end = range.endContainer;
    var commonAncestor = range.commonAncestorContainer;
    var nodes = [];
    var node;

    // walk parent nodes from start to common ancestor
    for (node = start.parentNode; node; node = node.parentNode)
    {
        if (node.nodeType == 3) //modified to only add text nodes to the array
            nodes.push(node);
        if (node == commonAncestor)
            break;
    }
    nodes.reverse();

    // walk children and siblings from start until end is found
    for (node = start; node; node = getNextNode(node))
    {
        if (node.nodeType == 3) //modified to only add text nodes to the array
            nodes.push(node);
        if (node == end)
            break;
    }

    return nodes;
}


function getNextNode(node, end)
{
    if (node.firstChild)
        return node.firstChild;
    while (node)
    {
        if (node.nextSibling)
            return node.nextSibling;
        node = node.parentNode;
    }
}



回答2:


Apollo in order to succeed that, you have to set an onclick listener when you create the span, by passing a callback function like

span.addEventListener("click",callbackwhenclickeventtrigger,false);
function callbackwhenclickeventtrigger(e){//pass as param the event target, inside this function create the un-bold function
}


来源:https://stackoverflow.com/questions/8429290/any-alternative-to-using-execcommand-for-highlighting-html-in-ios-ebook-reader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!