Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   Выделение в браузерах (https://javascript.ru/forum/offtopic/58606-vydelenie-v-brauzerakh.html)

ТОТ_САМЫЙ 30.09.2015 22:01

Выделение в браузерах
 
Ребят это баг или у меня руки кривые?

Задача:

1) сохранить выделение.
2) изменить значение текстовой ноды(эта процедура заставляет браузер сбросить выделение).
3) восстановить выделение.


Вот класс отвечающий за сохранение и восстановление выделения:

class Selection

	constructor: ->
		@ranges = []


	clear: ->
		@ranges = []


	save: ->
		@clear()
		selection = window.getSelection()
		for i in [0...selection.rangeCount]
			range = selection.getRangeAt(i)
			@ranges.push
				'startContainer': range.startContainer
				'startOffset': range.startOffset
				'endContainer': range.endContainer
				'endOffset': range.endOffset
		return


	restore: ->
		selection = window.getSelection()
		selection.removeAllRanges()
		for rangeData in @ranges
			range = document.createRange()
			range.setStart(rangeData.startContainer, rangeData.startOffset)
			range.setEnd(rangeData.endContainer, rangeData.endOffset)
			selection.addRange(range)
		return


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

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

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

Так же основной собственно вопрос, существует ли какая-то возможность определить направление выделения? Чтобы хотя бы в хроме и лисе знать в какую сторону рестор делать чтобы он выделение не сбрасывал. Дело в том что при сейве у них стартОффсет и ендОффсет расположены всегда в одинаковом порядке, старт всегда меньше енд, несзависимо оттого в какую сторону мы мышкой ведем.

ТОТ_САМЫЙ 30.09.2015 22:27

Оу, ну в общем как получить выделение в браузерах я нашел, то где кончается выделение показывается не в ренджах, а в селекшене

а именно

selection = getSelection()

// начало выделения
selection.anchorNode // нода 
selection.anchorOffset // символ

// конец выделения
selection.focusNode // нода 
selection.focusOffset // символ

ТОТ_САМЫЙ 30.09.2015 23:59

В итоге я написал работающий класс который сохраняет как обычное так и реверсивное выделение:

class Selection

	constructor: ->
		@anchorNode = null
		@anchorOffset = null
		@focusNode = null
		@focusOffset = null


	clear: ->
		@anchorNode = null
		@anchorOffset = null
		@focusNode = null
		@focusOffset = null


	save: ->
		selection = window.getSelection()
		@anchorNode = selection.anchorNode
		@anchorOffset = selection.anchorOffset
		@focusNode = selection.focusNode
		@focusOffset = selection.focusOffset
		return


	restore: ->
		selection = window.getSelection()
		selection.removeAllRanges()
		unless @anchorNode then return
		range = document.createRange()
		range.setStart(@anchorNode, @anchorOffset)
		selection.addRange(range)
		selection.extend(@focusNode, @focusOffset)
		return


В итоге мы ренджи не трогаем, мы у селекшена запоминаем стартовую и конечную точки, а потом при ресторе, мы создаем коллапсовый замкнутый рендж задавая ему лишь стартовую точку, и продолжаем (extend) его в нужном направлении до конечной точки, подобное extend заставляет нам сохранить направление выделения. И выделение под курсором не обрывается ^_^

Еще один момент в EDGE такое выделение сбрасывается при любой активности мышкой, по этому щас я добавлю проверку на то нажата ли клафиша мышки или отпущена. И если нажата то буду думать что делать

ТОТ_САМЫЙ 01.10.2015 00:50

Вот готовое решение:

class SelectionSaver


  isMs = /edge|msie|trident/i.test(navigator.userAgent)
  isInteractive = off

  if isMs
    addEventListener 'mousedown', =>
        isInteractive = on
    , on

    addEventListener 'mouseup', =>
        isInteractive = off
    , on


  constructor: ->
    @selection = getSelection()
    @anchorNode = null
    @anchorOffset = null
    @focusNode = null
    @focusOffset = null
    return


  clear: ->
    @anchorNode = null
    @anchorOffset = null
    @focusNode = null
    @focusOffset = null
    return


  save: ->
    @anchorNode = @selection.anchorNode
    @anchorOffset = @selection.anchorOffset
    @focusNode = @selection.focusNode
    @focusOffset = @selection.focusOffset
    return


  restore: ->
    if isMs and isInteractive then return
    @selection.removeAllRanges()
    unless @anchorNode then return
    range = document.createRange()
    range.setStart(@anchorNode, Math.min(@anchorOffset, @anchorNode.length))
    @selection.addRange(range)
    @selection.extend(@focusNode, Math.min(@focusOffset, @focusNode.length))
    return

ТОТ_САМЫЙ 01.10.2015 01:12

тадаам, ставим звезды https://github.com/Maxmaxmaximus/SelectionSaver


Часовой пояс GMT +3, время: 01:18.