2015-04-17 17 views
8

Próba stworzenia metody, która może odczytać bieżący widoczny tekst wewnątrz elementu. Metoda, którą widzicie poniżej, jest tak daleko, jak to było w ciągu ostatnich kilku dni.Uzyskiwanie widocznego tekstu w kolumnach CSS3

Czy jest coś bardziej niezawodnego w uzyskiwaniu widocznego tekstu w elemencie innym niż przy użyciu karetki/zakresu? Przyczyną problemu jest to, że mam dużo nadpisanego tekstu, który następnie zostanie wybrany, ponieważ caret nie przechwytuje textNode, ale zamiast niego kontener nadrzędny.

Przykładem tego, co moja strona wygląda & dlaczego mam problemy z metodą bieżącego:

enter image description here

  • Jak teraz Gael ma najbardziej przyjazny wydajność rozwiązanie & najłatwiejsze do wdrożenia.

Nie wiesz, ja tu sensu, inaczej daj mi znać :)


function getTextInColumn (rect) { 
     var startX = rect.left; 
     var startY = rect.top; 
     var endX = rect.left + rect.width - 2; 
     var endY = rect.top + rect.height - 2; 
     var start, end, range = null; 
     var i = 0; 
     var rangeText = ''; 

     while ((rangeText === '' && i < 100 && endY > 5)) { 
     range = null; 

     if (typeof document.caretPositionFromPoint != 'undefined') { 
      start = document.caretPositionFromPoint(startX, startY); 
      end = document.caretPositionFromPoint(endX, endY); 

      if (start !== null && end !== null) { 
      range = document.createRange(); 
      range.setStart(start.offsetNode, start.offset); 
      range.setEnd(end.offsetNode, end.offset); 
      } 
     } 
     else if (typeof document.caretRangeFromPoint != 'undefined') { 
      start = document.caretRangeFromPoint(startX, startY); 
      end = document.caretRangeFromPoint(endX, endY); 

      if (start !== null && end !== null) { 
      range = document.createRange(); 
      range.setStart(start.startContainer, start.startOffset); 
      range.setEnd(end.startContainer, end.startOffset); 
      } 
     } 

     if (range !== null) { 
      rangeText = range.toString(); 
     } 

     endY -= 52; 
     i++; 
     } 

     return rangeText; 
} 
+0

Nie jestem pewien, czy rozumiem: chcesz pobrać cały tekst znajdujący się w widocznej części strony? – Gael

+0

@Gael - Nie chcę przechwytywać całego widocznego tekstu wewnątrz elementu DOM, który jest pokazany na obrazku dołączonym do postu. Trudność polega na tym, że cały tekst mieści się w tym samym elemencie. –

+0

Czy możesz podać nam odpowiedni html? –

Odpowiedz

1

Globalnie, trzeba przetestować każdą literę, aby wiedzieć, czy jest widoczny. Ponieważ kontener blokowy może być częściowo widoczny, a wiedza, które części jego zawartości są widoczne, sugeruje ich oddzielne przetestowanie, aż do uzyskania ziarnistości literowej.
Jednak zamiast testować każdą literę, można sprawdzić, czy zestaw liter jest widoczny, i używając binary search method, zmniejszyć je tak, jak to jest konieczne, aby wiedzieć, czy wszystkie zawarte litery są widoczne.

pierwsze podejście

Stanowisko wymiar (np. Rect ograniczająca) o textNode nie znajduje się w jego właściwościach.

Tak więc początkowo próbowałem wstawić textNodes w elemencie bloku, ale to powodowało problemy z wydajnością, ponieważ modyfikowanie DOM implikuje reflow.

Co więcej, musimy nie tylko ustalić, czy węzeł tekstowy jest widoczny, ale także dla wszystkich jego liter. Włożyłem więc każdą literę w elemencie bloku:

var text= textNode.nodeValue; 
    var markedText= text.replace(/(.)/g,"<span class='marker'>$1</span>"); 
    var markedContainer= document.createElement("div"); 
    markedContainer.innerHTML= markedText; 

    textContainer.replaceChild(markedContainer,textNode); 

Dla tekstu, który zawiera tylko 10.000 liter, czas przetwarzania wynosił około 10s.

interfejs Zakres

Korzystanie ranges aby uzyskać pozycję zbioru listów wydaje się lepsze podejście, ponieważ nie musimy przebić się do drzewa DOM.

Zakres jest wstępnie zdefiniowany zawiera cały tekst:

var textNode = textContainer.childNodes[0]; //assumes that there is only one child and that it is a textNode 

    var range = document.createRange(); 
    range.selectNodeContents(textNode); 

następnie jako range ma takie same getBoundingClientRect metody jak pojemnika bloku, można sprawdzić, czy nie ma przecięcia w granicach okienka:

daje jaki zakres jest widoczny: całkowicie, częściowo, na none:

var intersection = intersectionArea(a, b); 
if (intersection == 0) 
    state= "NULL"; 
else { 
    //that means that a is completly in b 
    if (intersection == intersectionArea(a, a)) 
    state= "COMPLETE"; 
    else 
    state= "PARTIAL"; 
} 

Jeśli zakres jest częściowo widoczny, jest podzielony na dwa podzbiory, które będą następnie testowane, aż do uzyskania podzbiorów, które są widoczne lub nie, ale nie częściowo.

/* 
    ranges are indexed in an object by their startOffset property 
    */ 
    var ranges = { 0 : range }; 

    /* 
    rangesIdx contains all the ranges which have to be tested 
    */ 
    var rangesIdx = [0]; 

    while (rangesIdx.length > 0) { 

    var range = ranges[ rangesIdx.shift() ]; 

    switch (overlapsVisibleContent(range)){ 

     case "PARTIAL": 
     // if a range is partially visible, it is splitted on its middle 
     // the two resulting ranges will then be tested 
     if (range.endOffset - range.startOffset > 1){ 
      //even if one letter is not completly visible, it is considered to be completly. 
      var rangeLastPart= splitRange(range);     
      ranges[ rangeLastPart.startOffset ] = rangeLastPart; 

      rangesIdx.push(rangeLastPart.startOffset); 
      rangesIdx.push(range.startOffset); 
     }else 
       if(paintingMode) 
       paint(range.getBoundingClientRect(), "COMPLETE") 
     break; 
     case "COMPLETE":  
     // if a range is completly visible, it stays on the ranges object 
     break; 
     case "NULL": 
     // if a range is completly unvisible, it is deleted from the ranges object. 
     delete ranges[ range.startOffset ]; 
    } 
    } 

Przy tym samym tekście czas przetwarzania jest rzędu dziesiątek ms. A złożoność nie rośnie szczególnie wraz z długością tekstu.

Kompletny kod jest here

pewne zastrzeżenia

To po prostu widocznego tekstu z jednego textNode, ale ta sama logika wyszukiwania binarnego można zastosować do drzewa DOM.

Wszystkie widoczne zakresy są scalane na końcu procesu; może to być problem, ponieważ nie sąsiadujące ze sobą linie mogą być łączone.

+0

Niesamowite podejście @Gael - Przepisuję to, co do tej pory wykorzystałem, i sprawdzę, jak to działa. Ale jak pokazuje przykład .. to percet –

+1

To był dla mnie ciekawy temat;) – Gael

+1

Poprawiono go tak, że działa z zawartości HTML & So deweloper może wysłać prostokąt testowy do metody. http://jsfiddle.net/mnfyqjpv/2/ - to co najmniej skończyłem używać :) Twoje rozwiązanie działa o wiele lepiej niż moje poprzednie. Jest o wiele szybszy –

0

Jak Chyba tutaj, staramy się analizować DOM i uzyskać wszystkie elementy tekst Jeśli spojrzeć na następujące: jQuery.parseHTML()

i spojrzeć na właściwości elementu DOM DOM Element

Spójrz na przykład pobrać wartość tekstową DOM element tetxt value

Jeśli pisać kod HTML, dostaniesz lepsze pomoc

Nadzieja może pomóc

Powiązane problemy