Любая альтернатива использованию execCommand для выделения HTML в iOS для чтения электронных книг - PullRequest
2 голосов
/ 08 декабря 2011

Я разрабатываю программу для чтения книг для iOS, используя UIWebView. В данный момент я работаю с некоторыми основными файлами HTML, но в конечном итоге буду работать с ePubs. Я ищу подходящий способ оформления диапазонов текста. Мои диапазоны немного особенные в том смысле, что они обычно включают три диапазона - диапазон клавиш и диапазон непосредственно перед и диапазон сразу после. Распределение клавиш может охватывать несколько узлов и может начинаться или заканчиваться, например, при выделении жирным шрифтом и т. Д. Стиль не должен записываться в файл.

На данный момент у меня есть рабочее решение:

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();

Это работает нормально, но заметно медленно (на iPad2), даже если я изменяю его, чтобы выделить один диапазон в коротком HTML-документе. Всегда есть очень заметная задержка, прежде чем текст стилизован. Глядя на устройства для чтения электронных книг на iPad, такие как Kindle или iBooks, нет заметной задержки. Как они могут реализовать свою функцию выделения? Могут ли они читать географическое положение выбранного текста и накладывать какие-то наложения?

Я искал лучшее решение, чем уже использую, но безуспешно, поэтому, если у кого-то есть идея, я был бы очень признателен.

Спасибо!

Ответы [ 2 ]

8 голосов
/ 10 декабря 2011

Я сделал альтернативный метод выше, который выглядит намного быстрее. В основном он основан на превосходном коде mikeB, приведенном в этом вопросе.

Как получить узлы, лежащие внутри диапазона с помощью JavaScript?

  1. Сначала я разделяю начальный и конечный узлы, создавая новые текстовые узлы с только текст, который попадает в указанный диапазон. Я настраиваю диапазон для новых узлов.

  2. Я использую слегка измененную версию кода по ссылке выше вернуть массив text nodes, содержащийся в диапазоне.

  3. Я перебираю массив и помещаю промежутки со стилями вокруг каждого текста узел.

Насколько я тестировал, код работает хорошо. При одновременном применении стилей к нескольким диапазонам задержка сохраняется, но она намного лучше, чем раньше. Мне было бы интересно узнать, есть ли у кого-нибудь улучшения.

Мой код:

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] );
    }
}

Модифицированный код из кода mikeB:

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;
    }
}
0 голосов
/ 31 августа 2015

Apollo, чтобы преуспеть в этом, вы должны установить прослушиватель onclick при создании диапазона, передав функцию обратного вызова, такую ​​как

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