Установите позицию курсора на contentEditable <отделение>

В то время как я соглашаюсь, что постоянная потребность отсортировать карту является, вероятно, запахом, я думаю, что следующий код является самым легким способом сделать это, не используя различную структуру данных.

public class MapUtilities {

public static <K, V extends Comparable<V>> List<Entry<K, V>> sortByValue(Map<K, V> map) {
    List<Entry<K, V>> entries = new ArrayList<Entry<K, V>>(map.entrySet());
    Collections.sort(entries, new ByValue<K, V>());
    return entries;
}

private static class ByValue<K, V extends Comparable<V>> implements Comparator<Entry<K, V>> {
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

}

И вот смущающе неполный модульный тест:

public class MapUtilitiesTest extends TestCase {
public void testSorting() {
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("One", 1);
    map.put("Two", 2);
    map.put("Three", 3);

    List<Map.Entry<String, Integer>> sorted = MapUtilities.sortByValue(map);
    assertEquals("First", "One", sorted.get(0).getKey());
    assertEquals("Second", "Two", sorted.get(1).getKey());
    assertEquals("Third", "Three", sorted.get(2).getKey());
}

}

результатом является отсортированный список Карты. Объекты записи, из которых можно получить ключи и значения.

139
задан Community 23 May 2017 в 12:34
поделиться

3 ответа

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

var editable = document.getElementById('editable'),
    selection, range;

// Populates selection and range variables
var captureSelection = function(e) {
    // Don't capture selection outside editable region
    var isOrContainsAnchor = false,
        isOrContainsFocus = false,
        sel = window.getSelection(),
        parentAnchor = sel.anchorNode,
        parentFocus = sel.focusNode;

    while(parentAnchor && parentAnchor != document.documentElement) {
        if(parentAnchor == editable) {
            isOrContainsAnchor = true;
        }
        parentAnchor = parentAnchor.parentNode;
    }

    while(parentFocus && parentFocus != document.documentElement) {
        if(parentFocus == editable) {
            isOrContainsFocus = true;
        }
        parentFocus = parentFocus.parentNode;
    }

    if(!isOrContainsAnchor || !isOrContainsFocus) {
        return;
    }

    selection = window.getSelection();

    // Get range (standards)
    if(selection.getRangeAt !== undefined) {
        range = selection.getRangeAt(0);

    // Get range (Safari 2)
    } else if(
        document.createRange &&
        selection.anchorNode &&
        selection.anchorOffset &&
        selection.focusNode &&
        selection.focusOffset
    ) {
        range = document.createRange();
        range.setStart(selection.anchorNode, selection.anchorOffset);
        range.setEnd(selection.focusNode, selection.focusOffset);
    } else {
        // Failure here, not handled by the rest of the script.
        // Probably IE or some older browser
    }
};

// Recalculate selection while typing
editable.onkeyup = captureSelection;

// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
    editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
    if(editable.className.match(/\sselecting(\s|$)/)) {
        editable.className = editable.className.replace(/ selecting(\s|$)/, '');
        captureSelection();
    }
};

editable.onblur = function(e) {
    var cursorStart = document.createElement('span'),
        collapsed = !!range.collapsed;

    cursorStart.id = 'cursorStart';
    cursorStart.appendChild(document.createTextNode('—'));

    // Insert beginning cursor marker
    range.insertNode(cursorStart);

    // Insert end cursor marker if any text is selected
    if(!collapsed) {
        var cursorEnd = document.createElement('span');
        cursorEnd.id = 'cursorEnd';
        range.collapse();
        range.insertNode(cursorEnd);
    }
};

// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
    // Slight delay will avoid the initial selection
    // (at start or of contents depending on browser) being mistaken
    setTimeout(function() {
        var cursorStart = document.getElementById('cursorStart'),
            cursorEnd = document.getElementById('cursorEnd');

        // Don't do anything if user is creating a new selection
        if(editable.className.match(/\sselecting(\s|$)/)) {
            if(cursorStart) {
                cursorStart.parentNode.removeChild(cursorStart);
            }
            if(cursorEnd) {
                cursorEnd.parentNode.removeChild(cursorEnd);
            }
        } else if(cursorStart) {
            captureSelection();
            var range = document.createRange();

            if(cursorEnd) {
                range.setStartAfter(cursorStart);
                range.setEndBefore(cursorEnd);

                // Delete cursor markers
                cursorStart.parentNode.removeChild(cursorStart);
                cursorEnd.parentNode.removeChild(cursorEnd);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);
            } else {
                range.selectNode(cursorStart);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);

                // Delete cursor marker
                document.execCommand('delete', false, null);
            }
        }

        // Call callbacks here
        for(var i = 0; i < afterFocus.length; i++) {
            afterFocus[i]();
        }
        afterFocus = [];

        // Register selection again
        captureSelection();
    }, 10);
};
58
ответ дан 23 November 2019 в 23:21
поделиться

Обновление

Я написал кроссбраузерную библиотеку диапазонов и выбора под названием Rangy , которая включает улучшенную версию кода, который я разместил ниже. Вы можете использовать модуль сохранения и восстановления выделения для этого конкретного вопроса, хотя я бы хотел использовать что-то вроде ответа @Nico Burns , если вы больше ничего не делаете с выделениями в вашем проекте и вам не нужна большая часть библиотеки.

Предыдущий ответ

Вы можете использовать IERange ( http://code.google.com/p/ierange/ ) для преобразования IE TextRange в нечто вроде DOM Range и используйте его в сочетании с чем-то вроде отправной точки без век. Лично я бы использовал только алгоритмы из IERange, которые выполняют Range <-> TextRange, а не использовать все это целиком. И объект выбора IE не имеет свойств focusNode и anchorNode, но вместо этого вы должны иметь возможность просто использовать Range / TextRange, полученный из выбора.

Я мог бы собрать что-то вместе, чтобы сделать это, отправлю сюда, если и когда Да.

РЕДАКТИРОВАТЬ:

Я создал демонстрацию скрипта, который делает это. Он работает во всем, что я пробовал, за исключением ошибки в Opera 9, которую я еще не успел изучить. Он работает в браузерах IE 5.5, 6 и 7, Chrome 2, Firefox 2, 3 и 3.5 и Safari 4, все в Windows

http://www.timdown.co. uk / code / selections /

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

Я напишу это полностью в какой-то момент в ближайшее время.

19
ответ дан 23 November 2019 в 23:21
поделиться

Это решение работает во всех основных браузерах:

saveSelection() прикрепляется к событиям onmouseup и onkeyup div и сохраняет выбор в переменную savedRange.

restoreSelection() прикрепляется к событию onfocus div и повторно выбирает выбор, сохраненный в savedRange.

Это работает идеально, если только вы не хотите, чтобы выбор восстанавливался, когда пользователь щелкает по div, а также (что немного неинтуитивно, поскольку обычно вы ожидаете, что курсор будет перемещаться туда, куда вы щелкаете, но код включен для полноты)

Для достижения этого события onclick и onmousedown отменяются функцией cancelEvent(), которая является кроссбраузерной функцией для отмены события. Функция cancelEvent() также запускает функцию restoreSelection(), так как при отмене события click div не получает фокус, и поэтому ничего не выбирается, пока не будет запущена эта функция.

Переменная isInFocus хранит информацию о том, находится ли div в фокусе, и изменяется на "false" onblur и "true" onfocus. Это позволяет отменять события щелчка только в том случае, если div не находится в фокусе (иначе вы вообще не смогли бы изменить выделение).

Если вы хотите, чтобы выделение изменялось, когда div фокусируется по щелчку, и не восстанавливало выделение onclick (и только когда фокус задается элементу программно с помощью document.getElementById("area").focus(); или подобным образом, то просто удалите события onclick и onmousedown. Событие onblur и функции onDivBlur() и cancelEvent() также могут быть удалены в этих обстоятельствах.

Этот код должен работать, если его вставить непосредственно в тело html-страницы, если вы хотите быстро протестировать его:

<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
    if(window.getSelection)//non IE Browsers
    {
        savedRange = window.getSelection().getRangeAt(0);
    }
    else if(document.selection)//IE
    { 
        savedRange = document.selection.createRange();  
    } 
}

function restoreSelection()
{
    isInFocus = true;
    document.getElementById("area").focus();
    if (savedRange != null) {
        if (window.getSelection)//non IE and there is already a selection
        {
            var s = window.getSelection();
            if (s.rangeCount > 0) 
                s.removeAllRanges();
            s.addRange(savedRange);
        }
        else if (document.createRange)//non IE and no selection
        {
            window.getSelection().addRange(savedRange);
        }
        else if (document.selection)//IE
        {
            savedRange.select();
        }
    }
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
    isInFocus = false;
}

function cancelEvent(e)
{
    if (isInFocus == false && savedRange != null) {
        if (e && e.preventDefault) {
            //alert("FF");
            e.stopPropagation(); // DOM style (return false doesn't always work in FF)
            e.preventDefault();
        }
        else {
            window.event.cancelBubble = true;//IE stopPropagation
        }
        restoreSelection();
        return false; // false = IE style
    }
}
</script>
96
ответ дан 23 November 2019 в 23:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: