2012-12-19 22 views
15

複数のタグにまたがるHTMLテキストの背景を変更するなどの変更を保存する方法はありますか?変更を保存するには、HTMLページ。HTMLで選択した後に範囲オブジェクトの変更を保持する

EDIT:詳しい説明。

HTMLページがロードされると、テキストが選択された範囲の対象とexecuteCommandを用いて強調表示される:ページがリロードされるまで

   document.execCommand("BackColor", false, 'yellow'); 

変化(黄色などのテキストを強調表示)が残ります。しかし、ページがリロードされると、これらの変更はそこにはありません。私が欲しいのは、ローカルDBのsqliteのようにこれらの変更をどうにかして保存することです。その結果、ページが再読み込み/更新されたときにHTMLページの変更が表示されるはずです。

どのようにするか考えてください。範囲開始オフセットと終了オフセットを保存する必要があります。これは、次回ページが読み込まれる範囲を作成するために使用できます。あなたの洞察力を与えてください。

+0

あなたはユーザー1人当たりですか? – cowls

+0

これらの変更はどのようにして行われましたか? –

+0

あまり正確ではないことを申し訳ありません。今私の質問を編集しました。 – Satish

答えて

52

、あなたは文字オフセットに選択した範囲をシリアル化し、このようなものを使用してリロードに再びそれをデシリアライズできます

デモ:http://jsfiddle.net/WeWy7/3/

コード:

var saveSelection, restoreSelection; 

if (window.getSelection && document.createRange) { 
    saveSelection = function(containerEl) { 
     var range = window.getSelection().getRangeAt(0); 
     var preSelectionRange = range.cloneRange(); 
     preSelectionRange.selectNodeContents(containerEl); 
     preSelectionRange.setEnd(range.startContainer, range.startOffset); 
     var start = preSelectionRange.toString().length; 

     return { 
      start: start, 
      end: start + range.toString().length 
     }; 
    }; 

    restoreSelection = function(containerEl, savedSel) { 
     var charIndex = 0, range = document.createRange(); 
     range.setStart(containerEl, 0); 
     range.collapse(true); 
     var nodeStack = [containerEl], node, foundStart = false, stop = false; 

     while (!stop && (node = nodeStack.pop())) { 
      if (node.nodeType == 3) { 
       var nextCharIndex = charIndex + node.length; 
       if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { 
        range.setStart(node, savedSel.start - charIndex); 
        foundStart = true; 
       } 
       if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { 
        range.setEnd(node, savedSel.end - charIndex); 
        stop = true; 
       } 
       charIndex = nextCharIndex; 
      } else { 
       var i = node.childNodes.length; 
       while (i--) { 
        nodeStack.push(node.childNodes[i]); 
       } 
      } 
     } 

     var sel = window.getSelection(); 
     sel.removeAllRanges(); 
     sel.addRange(range); 
    } 
} else if (document.selection) { 
    saveSelection = function(containerEl) { 
     var selectedTextRange = document.selection.createRange(); 
     var preSelectionTextRange = document.body.createTextRange(); 
     preSelectionTextRange.moveToElementText(containerEl); 
     preSelectionTextRange.setEndPoint("EndToStart", selectedTextRange); 
     var start = preSelectionTextRange.text.length; 

     return { 
      start: start, 
      end: start + selectedTextRange.text.length 
     } 
    }; 

    restoreSelection = function(containerEl, savedSel) { 
     var textRange = document.body.createTextRange(); 
     textRange.moveToElementText(containerEl); 
     textRange.collapse(true); 
     textRange.moveEnd("character", savedSel.end); 
     textRange.moveStart("character", savedSel.start); 
     textRange.select(); 
    }; 
} 
+0

@Terry:ああ、申し訳ありませんが、私は答えがRangyを持っていたことを忘れていました。 –

+0

@Terry:更新されました。 –

+6

@ Tim Down:このソリューションは本当に非常に役に立ちます。あなたは偉大な人です。 – Satish

1

コンテキストについて詳しく知ることなく、正確な答えを出すのは難しいですが、可能ですが、ほとんどの場合、非常に複雑になります。ユースケースによっては、いくつかの方法があります。

クッキーやローカルストレージ

あなたがクライアント側記憶(クッキー、ローカルストレージまたは類似)のいくつかの並べ替えを使用して、要素が変更され、どのようにされたかについての情報を救うことができます。ページがリロードされるたびに、そのストレージを読み取り、変更を適用します。それをどのように実装するかは、それらの変更がどのように行われるかにかかっており、私が恐れる一つのSO答えでカバーするためには広範囲に及ぶでしょう。

サーバ側記憶

あなたは(あなたが認証のいくつかのフォームを持っている)、各ユーザが誰であるかを知っていれば、彼らは何か(ただし作られる)の外観を変更するたびに、あなたは、Ajaxリクエストを作りますこれらの変更をデータベースに保存します。その後の各ページのロード時には、リクエストを作成する用途を確認し、データベースで検索を行って変更が行われているかどうかを確認し、必要に応じて適用する必要があります。

クライアント側とサーバー側の両方のストレージソリューションに共通しているのは、実装するにはかなり広範囲になるということです。行くための別の方法は、ユーザーがWebページをレンダリングする方法をカスタマイズすることができGreasemonkey for Firefoxのようなプラグインを利用することだろう

ブラウザプラグイン

。これらのカスタマイズは、ページ読み込みの間永続的になります。各選択のため

+0

オフラインアプリケーションであるため、ローカルストレージがオプションになります。 – Satish

1

文字を使用しますカーソルが新しい段落の先頭にある場合、オフセットは機能しません。以下のアプローチは、DOMノードを歩き、すべてのノードをオフセットに向かって数えます。また、開始点と終了点を個別に処理して、選択範囲が正確な位置を覚えていることを確認します。

/* 
Gets the offset of a node within another node. Text nodes are 
counted a n where n is the length. Entering (or passing) an 
element is one offset. Exiting is 0. 
*/ 
var getNodeOffset = function(start, dest) { 
    var offset = 0; 

    var node = start; 
    var stack = []; 

    while (true) { 
    if (node === dest) { 
     return offset; 
    } 

    // Go into children 
    if (node.firstChild) { 
     // Going into first one doesn't count 
     if (node !== start) 
     offset += 1; 
     stack.push(node); 
     node = node.firstChild; 
    } 
    // If can go to next sibling 
    else if (stack.length > 0 && node.nextSibling) { 
     // If text, count length (plus 1) 
     if (node.nodeType === 3) 
     offset += node.nodeValue.length + 1; 
     else 
     offset += 1; 

     node = node.nextSibling; 
    } 
    else { 
     // If text, count length 
     if (node.nodeType === 3) 
     offset += node.nodeValue.length + 1; 
     else 
     offset += 1; 

     // No children or siblings, move up stack 
     while (true) { 
     if (stack.length <= 1) 
      return offset; 

     var next = stack.pop(); 

     // Go to sibling 
     if (next.nextSibling) { 
      node = next.nextSibling; 
      break; 
     } 
     } 
    } 
    } 
}; 

// Calculate the total offsets of a node 
var calculateNodeOffset = function(node) { 
    var offset = 0; 

    // If text, count length 
    if (node.nodeType === 3) 
    offset += node.nodeValue.length + 1; 
    else 
    offset += 1; 

    if (node.childNodes) { 
    for (var i=0;i<node.childNodes.length;i++) { 
     offset += calculateNodeOffset(node.childNodes[i]); 
    } 
    } 

    return offset; 
}; 

// Determine total offset length from returned offset from ranges 
var totalOffsets = function(parentNode, offset) { 
    if (parentNode.nodeType == 3) 
    return offset; 

    if (parentNode.nodeType == 1) { 
    var total = 0; 
    // Get child nodes 
    for (var i=0;i<offset;i++) { 
     total += calculateNodeOffset(parentNode.childNodes[i]); 
    } 
    return total; 
    } 

    return 0; 
}; 

var getNodeAndOffsetAt = function(start, offset) { 
    var node = start; 
    var stack = []; 

    while (true) { 
    // If arrived 
    if (offset <= 0) 
     return { node: node, offset: 0 }; 

    // If will be within current text node 
    if (node.nodeType == 3 && (offset <= node.nodeValue.length)) 
     return { node: node, offset: Math.min(offset, node.nodeValue.length) }; 

    // Go into children (first one doesn't count) 
    if (node.firstChild) { 
     if (node !== start) 
     offset -= 1; 
     stack.push(node); 
     node = node.firstChild; 
    } 
    // If can go to next sibling 
    else if (stack.length > 0 && node.nextSibling) { 
     // If text, count length 
     if (node.nodeType === 3) 
     offset -= node.nodeValue.length + 1; 
     else 
     offset -= 1; 

     node = node.nextSibling; 
    } 
    else { 
     // No children or siblings, move up stack 
     while (true) { 
     if (stack.length <= 1) { 
      // No more options, use current node 
      if (node.nodeType == 3) 
      return { node: node, offset: Math.min(offset, node.nodeValue.length) }; 
      else 
      return { node: node, offset: 0 }; 
     } 

     var next = stack.pop(); 

     // Go to sibling 
     if (next.nextSibling) { 
      // If text, count length 
      if (node.nodeType === 3) 
      offset -= node.nodeValue.length + 1; 
      else 
      offset -= 1; 

      node = next.nextSibling; 
      break; 
     } 
     } 
    } 
    } 
}; 

exports.save = function(containerEl) { 
    // Get range 
    var selection = window.getSelection(); 
    if (selection.rangeCount > 0) { 
    var range = selection.getRangeAt(0); 
    return { 
     start: getNodeOffset(containerEl, range.startContainer) + totalOffsets(range.startContainer, range.startOffset), 
     end: getNodeOffset(containerEl, range.endContainer) + totalOffsets(range.endContainer, range.endOffset) 
    }; 
    } 
    else 
    return null; 
}; 

exports.restore = function(containerEl, savedSel) { 
    if (!savedSel) 
    return; 

    var range = document.createRange(); 

    var startNodeOffset, endNodeOffset; 
    startNodeOffset = getNodeAndOffsetAt(containerEl, savedSel.start); 
    endNodeOffset = getNodeAndOffsetAt(containerEl, savedSel.end); 

    range.setStart(startNodeOffset.node, startNodeOffset.offset); 
    range.setEnd(endNodeOffset.node, endNodeOffset.offset); 

    var sel = window.getSelection(); 
    sel.removeAllRanges(); 
    sel.addRange(range); 
}; 

最近のブラウザ上でのみ動作(少なくともIE 9+):ここで私は主要なプロジェクト(末尾の機能を参照)で使用更新されたバージョンです。

+0

'' 'var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); '' 'これはIE10でエラーをスローします:**エラー800a025eのために操作を完了できませんでした** – mayankcpdixit

+0

' containerEl 'はどこから来ましたか= _ =! – newBike

+0

私はバグ修正とFirefoxの変更でそれを更新しました。 「containerEl」はコンテナ要素(例えばコンテンツ編集可能ノード)であり、 –

関連する問題