2015-12-11 59 views
5

Próbuję uzyskać przesunięcia znaków dla początku i końca zaznaczenia w obrębie artykułu. Chciałbym jednak, aby niektóre elementy zostały zignorowane w tym procesie.Pobranie zaznaczonego tekstu z wyjątkiem elementów nie do wybrania

W mojej aplikacji serwer pracuje z kodem HTML, zanim JS wstrzyknie kilka elementów dynamicznie i jest bardzo ważne, aby liczba znaków w tekście pozostawała spójna między serwerem a klientem.

Miałem nadzieję, że będzie to tak proste jak window.getSelection() wraz z user-select: none;. Niestety, mimo że tekst nie wygląda na wybrany, nadal znajduje się w zakresie wyboru.

Napisałem krótki przykład poniżej. Miałem szansę na napisanie removeFromSelection jako obejścia bez większego powodzenia. Być może muszę usunąć zakresy, które nakładają się na .unselectable i ręcznie wypełnić luki nowymi obiektami zasięgu. Czuję, że to powinno być łatwiejsze niż robię to. Jak powinienem to robić?

function findAncestorOffset(container, node, offset) 
 
{ 
 
\t if (node == container) 
 
\t \t return offset; 
 
\t var parent = node.parentNode; 
 
\t var syblings = parent.childNodes; 
 
\t for (var i = 0, len = syblings.length; i < len; i++) 
 
\t { 
 
\t \t if (syblings[i] == node) 
 
\t \t \t break; 
 
\t \t offset += $(syblings[i]).text().length; 
 
\t } 
 
\t return findAncestorOffset(container, parent, offset); 
 
} 
 

 
function removeFromSelection(selector, selection) 
 
{ 
 
\t $(selector).each(function(i, node){ 
 
\t \t var range = document.createRange(); 
 
\t \t range.selectNodeContents(node); 
 
\t \t selection.removeAllRanges(range); 
 
\t }); 
 
} 
 

 
var onSelect = function(){ 
 
    var sel = window.getSelection(); 
 
    //removeFromSelection('.unselectable', sel); 
 
    var text = sel.toString(); 
 
    $('#out').text(text); 
 
    var range = sel.getRangeAt(0).cloneRange(); 
 
    var i = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset); 
 
    $('#from').text(i); 
 
    $('#to').text(i + text.length); 
 
} 
 
$('.article').mouseup(onSelect); 
 
$('.article').focusout(onSelect);
.unselectable { 
 
\t -webkit-touch-callout: none; 
 
\t -webkit-user-select: none; 
 
\t -khtml-user-select: none; 
 
\t -moz-user-select: none; 
 
\t -ms-user-select: none; 
 
\t user-select: none; 
 
} 
 

 
pre, .article { 
 
    border: solid 1px black; 
 
    padding: 12px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<h1>Select This</h1> 
 
<div class="article"> 
 
<p> 
 
    This can be selected. 
 
</p> 
 
<p class="unselectable"> 
 
    This can't. 
 
</p> 
 
<p> 
 
    And this can again. 
 
</p> 
 
</div> 
 

 
<h1>Output</h1> 
 
<div> 
 
    From: <span id="from"></span>, To: <span id="to"></span> 
 
</div> 
 
<pre id="out"> 
 
</pre>

+1

Powinieneś dostać wybrany HTML: http://stackoverflow.com/questions/5222814/window-getselection-return-html Następnie można filtrować go, np: http://jsfiddle.net/xpca4fp5/ Ale to wymagałoby więcej testów, ponieważ myślę, że to może być błędne –

Odpowiedz

0

Cóż, nie jest to pełne rozwiązanie, ale to dopiero początek. Pomysł polega na iteracji po każdym elemencie akapitu, a jeśli jest to unselectable, usuń go z zaznaczenia. Konwertuję zaznaczenie na tablicę znaków i umieszczam ją na początku, aby indeks odpowiadał indeksowi w całym dokumencie.

Nie zwracałem uwagi na ostateczne wartości to i from, prawdopodobnie nie mają racji - ale co ważniejsze, jeśli zaznaczysz cały tekst, zobaczysz, że jest wyłączony przez kilka znaków na drugim niewybaczalnym blok. Nie mam czasu, żeby się z tym pogodzić, ale może ktoś inny może podnieść miejsce, w którym skończyłem.

function findAncestorOffset(container, node, offset) 
 
{ 
 
\t if (node == container) 
 
\t \t return offset; 
 
\t var parent = node.parentNode; 
 
\t var syblings = parent.childNodes; 
 
\t for (var i = 0, len = syblings.length; i < len; i++) 
 
\t { 
 
\t \t if (syblings[i] == node) 
 
\t \t \t break; 
 
\t \t offset += $(syblings[i]).text().length; 
 
\t } 
 
\t return findAncestorOffset(container, parent, offset); 
 
} 
 

 
var onSelect = function(){ 
 
    var sel = window.getSelection(); 
 

 
    var textArray = sel.toString().split(''); 
 
    var range = sel.getRangeAt(0).cloneRange(); 
 
    var from = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset); 
 
    var to = from + textArray.length; 
 
    textArray = (new Array(from)).concat(textArray); 
 
    
 
    var i = 0; 
 
    $('.article p').each((_, rawElement) => { 
 
    var element = $(rawElement); 
 
    var sectionStart = i; 
 
    var lengthOfSection = element.text().length; 
 
    if(element.hasClass('unselectable')) { 
 
     textArray.splice(sectionStart, lengthOfSection); 
 
    } else { 
 
     i += lengthOfSection; 
 
    } 
 
    }); 
 
    
 
    var text = textArray.join(''); 
 
    
 
    $('#from').text(from); 
 
    $('#to').text(to); 
 
    $('#out').text(text); 
 
} 
 
$('.article').mouseup(onSelect); 
 
$('.article').focusout(onSelect);
.unselectable { 
 
\t -webkit-touch-callout: none; 
 
\t -webkit-user-select: none; 
 
\t -khtml-user-select: none; 
 
\t -moz-user-select: none; 
 
\t -ms-user-select: none; 
 
\t user-select: none; 
 
} 
 

 
pre, .article { 
 
    border: solid 1px black; 
 
    padding: 12px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<h1>Select This</h1> 
 
<div class="article"> 
 
<p> 
 
    This can be selected. 
 
</p> 
 
<p class="unselectable"> 
 
    This can't. 
 
</p> 
 
<p> 
 
    And this can again. 
 
</p> 
 
<p> 
 
    This can be selected. 
 
</p> 
 
<p class="unselectable"> 
 
    This can't. 
 
</p> 
 
<p> 
 
    And this can again. 
 
</p> 
 
</div> 
 

 
<h1>Output</h1> 
 
<div> 
 
    From: <span id="from"></span>, To: <span id="to"></span> 
 
</div> 
 
<pre id="out"> 
 
</pre>