2010-05-22 4 views
8

iPhone의 UIWebView에서 간단한 스타일이 적용된 텍스트를 html로 표시하고 있습니다. 그것은 기본적으로 때로는 강하고 강조된 문구가있는 일련의 문단입니다. 런타임에는 텍스트 범위에 스타일을 적용해야합니다.uiwebview에서 javascript로 텍스트 범위에 스타일 적용

비슷한 시나리오가 있으며 그 중 하나가 검색 결과를 강조 표시합니다. 사용자가 "무언가"를 검색했다면 단어의 배경 뒤에서 배경색을 변경하고 나중에 원래 배경을 복원하고 싶습니다.

자바 스크립트를 사용하여 텍스트 범위에 스타일을 적용 할 수 있습니까? 이것의 핵심 부분은 또한 스타일을 해제 할 수 있습니다.

따라야 할 두 가지 경로가있는 것 같습니다. 하나는 Objective-C에서 일부 html을 수정하고 javascript를 통해 일부 컨테이너의 새로운 innerHTML로 전달하는 것입니다. 다른 하나는 javascript를 사용하여 DOM 노드를 직접 조작하는 것입니다.

html을 조작 할 수는 있지만 Objective-C에서는 지루한 것처럼 들리므로 합리적인 방법이라면 DOM을 조작하는 것이 좋습니다. 나는 자바 스크립트와 DOM에 익숙하지 않아서 합리적인 접근법인지는 모르겠다.

텍스트 범위와 오프셋이있는 노드 범위 사이를 변환하는 루틴을 작성했습니다. 따라서 텍스트 범위 100-200에서 시작하여 한 단락에서 시작하여 세 번째 단락으로 끝나는 경우 주어진 텍스트 범위를 나타내는 노드 내에서 텍스트 노드와 오프셋을 가져올 수 있습니다. 텍스트의 오프셋에서 텍스트 노드를 분할하는 방법이 필요합니다. 현재 텍스트 범위가 포함 된 단락에 스타일을 적용하기 만하면됩니다.

몇 가지 참고 사항 : JQuery와 같은

  • 바로 자바 스크립트 제발, 아니 외부 프레임 워크.
  • 변경 사항을 디스크에 쓸 필요가 없습니다.
  • 변경 사항은 실행 취소 가능하거나 적어도 제거 가능해야합니다.
  • 적용 할 스타일이 이미 CSS 파일에 있습니다.
  • iPhone 3.0 이상에서 작동해야합니다.
  • 모든 소스 파일은 앱과 함께 제공됩니다.
  • 자세한 정보를 입력하십시오.

의견을 보내 주셔서 감사합니다.

답변

19

당신이 이것을 위해 완전한 해결책을 얻으려고 많이 요구하고 있다고 생각하지만, 재미있어 보였습니다. 그래서 구현했습니다. 다음은 OS 3.0을 실행하는 iPhone의 Safari를 포함하여 최근 WebKit 브라우저에서 작동합니다. 그것은 비표준이지만 편리한 intersectsNode 방법을 사용합니다. Range은 WebKit에 있지만 3.0의 Firefox에서 제거되었으므로 최신 버전의 Firefox에서는 작동하지 않지만 그렇게 할 수는 있습니다.

다음은 각 선택된 텍스트 노드를 "someclass"클래스의 <span> 요소로 둘러 쌉니다. 또한 쉽게 되돌리기를 허용하는 고유 한 클래스도 있습니다. applyClassToSelection은이 고유 한 클래스를 반환합니다. 이 클래스를 removeSpansWithClass에 전달하여 범위를 제거하십시오.

UPDATE : 지금 테스트 OS 3.0을 실행 아이폰에서 작동 : 선택은 전적으로 하나의 텍스트 노드

업데이트 2에 포함되어 고정 문제.

업데이트 3 :rangeIntersectsNode Firefox 3.0 이상을 지원하는 기능이 추가되었습니다. 이 코드는 이제 Firefox 1.0 이상, Safari 3.1 이상, Google Chrome, Opera 9.6 이상 및 기타 (아직까지 테스트되지 않음)에서 작동합니다. Internet Explorer에서 전혀 작동하지 않으며 해당 브라우저에서 오류가 발생합니다. 곧 IE 버전을 사용할 계획입니다.

<script type="text/javascript"> 
    var nextId = 0; 

    var rangeIntersectsNode = (typeof window.Range != "undefined" 
      && Range.prototype.intersectsNode) ? 

     function(range, node) { 
      return range.intersectsNode(node); 
     } : 

     function(range, node) { 
      var nodeRange = node.ownerDocument.createRange(); 
      try { 
       nodeRange.selectNode(node); 
      } catch (e) { 
       nodeRange.selectNodeContents(node); 
      } 

      return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 && 
       range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1; 
     }; 

    function applyClassToSelection(cssClass) { 
     var uniqueCssClass = "selection_" + (++nextId); 
     var sel = window.getSelection(); 
     if (sel.rangeCount < 1) { 
      return; 
     } 
     var range = sel.getRangeAt(0); 
     var startNode = range.startContainer, endNode = range.endContainer; 

     // Split the start and end container text nodes, if necessary 
     if (endNode.nodeType == 3) { 
      endNode.splitText(range.endOffset); 
      range.setEnd(endNode, endNode.length); 
     } 

     if (startNode.nodeType == 3) { 
      startNode = startNode.splitText(range.startOffset); 
      range.setStart(startNode, 0); 
     } 

     // Create an array of all the text nodes in the selection 
     // using a TreeWalker 
     var containerElement = range.commonAncestorContainer; 
     if (containerElement.nodeType != 1) { 
      containerElement = containerElement.parentNode; 
     } 

     var treeWalker = document.createTreeWalker(
      containerElement, 
      NodeFilter.SHOW_TEXT, 
      // Note that Range.intersectsNode is non-standard but 
      // implemented in WebKit 
      function(node) { 
       return rangeIntersectsNode(range, node) ? 
        NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 
      }, 
      false 
     ); 

     var selectedTextNodes = []; 
     while (treeWalker.nextNode()) { 
      selectedTextNodes.push(treeWalker.currentNode); 
     } 

     var textNode, span; 

     // Place each text node within range inside a <span> 
     // element with the desired class 
     for (var i = 0, len = selectedTextNodes.length; i < len; ++i) { 
      textNode = selectedTextNodes[i]; 
      span = document.createElement("span"); 
      span.className = cssClass + " " + uniqueCssClass; 
      textNode.parentNode.insertBefore(span, textNode); 
      span.appendChild(textNode); 
     } 

     return uniqueCssClass; 
    } 

    function removeSpansWithClass(cssClass) { 
     var spans = document.body.getElementsByClassName(cssClass), 
      span, parentNode; 

     // Convert spans to an array to prevent live updating of 
     // the list as we remove the spans 
     spans = Array.prototype.slice.call(spans, 0); 

     for (var i = 0, len = spans.length; i < len; ++i) { 
      span = spans[i]; 
      parentNode = span.parentNode; 
      parentNode.insertBefore(span.firstChild, span); 
      parentNode.removeChild(span); 

      // Glue any adjacent text nodes back together 
      parentNode.normalize(); 
     } 
    } 

    var c; 
</script> 

<input type="button" onclick="c = applyClassToSelection('someclass')" 
    value="Add class"> 
<input type="button" onclick="removeSpansWithClass(c)" 
    value="Remove class"> 
+0

감사합니다. 이것은 내가 원하는 것 이상입니다. – drawnonward

+0

아마도 이것을 github에 넣을 수 있습니다. – Mark

+1

개선 된 버전이 포함될 크로스 브라우저 범위/선택 라이브러리에서 작업 중입니다. 아직 진행 단계는 아니지만 다음과 같이 Google 코드 프로젝트를 만들었습니다. http://code.google.com/p/rangy/ –

관련 문제