2015-04-22 12 views
6

Tôi đã đọc số excellent answer này cho cùng một câu hỏi. Tuy nhiên, tôi đã thử mọi kỹ thuật mà @Reinmar được đề xuất và không có kỹ thuật nào trong số đó có vẻ hoạt động.CKEDITOR - không thể khôi phục vị trí con trỏ sau khi sửa đổi DOM

Tình huống là tôi đang lấy HTML hiện tại từ trình chỉnh sửa và gói các phần nhất định trong các thẻ span. Sau đó tôi đặt lại HTML đã sửa đổi và cố gắng khôi phục vị trí con trỏ của người dùng. Không có kỹ thuật nào hoạt động.

Dưới đây là một ví dụ rất đơn giản để tạo lại vấn đề:

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <title></title> 
    <script src="//cdn.ckeditor.com/4.4.7/standard/ckeditor.js"></script> 

</head> 
<body> 
    <textarea id="cktest"><p>Sometimes Lorem. Sometime Ipsum. Always dolor.</p></textarea> 

    <script type="text/javascript"> 

     (function() { 
      var checkTimeout; 
      var bookmark; 

      var storeCursorLocation = function(editor) { 
       bookmark = editor.getSelection().createBookmarks(); 
      }; 

      var restoreCursorLocation = function(editor) { 
       editor.getSelection().selectBookmarks(bookmark); 
      }; 

      var validateText = function(editor) { 
       storeCursorLocation(editor); 
       var data = editor.document.getBody().getHtml(); 
       data = data.replace("Lorem", "<span class='err-item'>Lorem</span>"); 
       editor.document.getBody().setHtml(data); 
       restoreCursorLocation(editor); 
      }; 


      CKEDITOR.replace('cktest', { 
       on: { 
        'instanceReady': function(evt) { 

        }, 
        'key' : function(evt) { 
         clearTimeout(checkTimeout); 
         checkTimeout = setTimeout(function() { 
          validateText(evt.editor); 
         }, 1000); 
        } 
       } 
      }); 
     })(); 

    </script> 
</body> 
</html> 

Mã này bắt đầu một bộ đếm thời gian khi người dùng nhấn một phím, và sau đó chờ 1 giây sau khi họ dừng nhấn phím để làm việc kiểm tra.

Sao chép tệp này vào tệp .html mới và chạy tệp đó trong trình duyệt yêu thích của bạn (Tôi đang sử dụng Chrome).

Khi CKEditor tải, sử dụng chuột để đặt con trỏ của bạn ở đâu đó ở giữa văn bản. Sau đó nhấn phím CTRL và đợi 1 giây. Bạn sẽ thấy con trỏ nhảy trở lại phần đầu của văn bản.

mã ví dụ này sử dụng

editor.getSelection().createBookmarks(); 

để tạo ra các bookmark. Nhưng tôi cũng đã cố gắng:

editor.getSelection().createBookmarks(true); 

editor.getSelection().createBookmarks2(); 

Tôi cũng đã cố gắng chỉ tiết kiệm phạm vi sử dụng

var ranges = editor.getSelection().getRanges(); 

editor.getSelection().selectRanges(ranges); 

trong hàm restoreCursorLocation.

Trả lời

5
 (function() { 
      var checkTimeout; 
      var bookmark; 

      var storeCursorLocation = function(editor) { 
       bookmark = editor.getSelection().createBookmarks(true); 
      }; 

      var restoreCursorLocation = function(editor) { 
       //editor.focus(); 
       editor.getSelection().selectBookmarks(bookmark); 
      }; 

      var validateText = function(editor) { 
       storeCursorLocation(editor); 

       var data = editor.document.getBody().getHtml(); 
       data = data.replace("spaceflight", "<span class='err-item'>spaceflight</span>"); 
       editor.document.getBody().setHtml(data); 
       restoreCursorLocation(editor); 
       //fire this event after DOM changes if working with widgets 
       //editor.fire('contentDomInvalidated'); 
      }; 


      var editor = CKEDITOR.replace('editor1', { 
       extraAllowedContent : 'span(err-item)',    
       on: { 
        "pluginsLoaded" : function(event){ 
         editor.on('contentDom', function() { 
          var editable = editor.editable();     
          editable.attachListener(editable, 'keyup', function(e) { 
           clearTimeout(checkTimeout); 
           checkTimeout = setTimeout(function() { 
            validateText(editor); 
           }, 100); 
          }); 
         }); 
        } 
       } 
      }); 
     })(); 

Tôi đã kiểm tra mã của bạn, thực hiện một số chỉnh sửa và ở trên có vẻ hoạt động tốt. Tôi biết bạn nói rằng bạn đã thử nó nhưng đối với tôi createBookmarks(true) đã thực hiện các trick.

viên thuyết minh và Ghi chú:

  1. Bạn cần thiết để sử dụng createBookmarks(true) mà chèn khoảng độc đáo vào HTML. Dấu trang đó không bị ảnh hưởng bởi những thay đổi bạn đang thực hiện bên trong DOM (có các giới hạn của khóa học, ví dụ: thay đổi tùy chỉnh của bạn sẽ xóa dấu trang).
  2. Rất thông minh khi sử dụng getBody().getHtml()getBody().setHTML(). Nếu bạn đã sử dụng editor.getData(), điều này sẽ xóa các khoảng trống rỗng đại diện cho dấu trang. Tuy nhiên, xin lưu ý rằng cách tiếp cận như vậy có thể phá vỡ các tiện ích vì vậy cần phải kích hoạt sự kiện contentDomInvalidated sau các thay đổi đó.
  3. Tôi cũng đã tập trung biên tập trước khi khôi phục lựa chọn nhưng đây là giải pháp "chỉ trong trường hợp", vì tôi đã nhận thấy rằng trình soạn thảo chọn dấu trang mà không có nó. Tuy nhiên, vì một lý do nào đó, bạn đang mất lựa chọn, đây sẽ là một thứ khác để sử dụng.

dụ đây bạn đã làm việc: http://jsfiddle.net/j_swiderski/nwbsywnn/1/

+0

Chỉ cần thêm vào câu trả lời này - một giải pháp an toàn hơn nhiều là sử dụng ['CKEDITOR.style'] (http://docs.ckeditor.com/#!/api/CKEDITOR.style), bởi vì nó sẽ chỉ chạm vào những phần này của cây phải được thay đổi. Tuy nhiên, để sử dụng nó, bạn sẽ cần phải thực hiện một chức năng tìm kiếm một văn bản trong DOM, đó là một điều khá phức tạp. – Reinmar

+0

Cảm ơn câu trả lời tuyệt vời, Reinmar. Trong mã thực, tôi đang sử dụng CKEDITOR.htmlParser để xem qua tài liệu và thực hiện các sửa đổi. Không chỉ làm đơn giản .replace(). Tôi sẽ xem xét việc sử dụng các kiểu và xem nó có hoạt động tốt hơn không. Không chắc chắn tại sao createBookmark (true) không hoạt động cho tôi trước đây. Nhưng bây giờ. –

1

Kiểm tra hành vi mặc định khi bạn thiết innerHtml trong https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

Loại bỏ tất cả các trẻ em phần tử, phân tích các chuỗi nội dung và gán các nút kết quả như trẻ em của nguyên tố này

Các bookmark trong ckeditor là các phần tử span ẩn và thiết lập innerHtml sẽ loại bỏ tất cả các phần tử đó.

Dù sao giải pháp cũng rất đơn giản.

Thay đổi chức năng storeCursorLocation của bạn để này

var storeCursorLocation = function(editor) { 
    bookmark = editor.getSelection().createBookmarks(true); 
}; 

Khi bạn vượt qua đúng như thông số nó sẽ sử dụng các id như tài liệu tham khảo thay vì lưu trữ các phần tử DOM, do đó bạn có thể khôi phục sau đó sau khi một sự thay đổi innerHtml.

{Sửa}

Reading Giải pháp 2 từ @Reinmar ông nói

Nếu bạn có thể tránh những thay đổi innerHTML không kiểm soát được và thay vào đó nối thêm/xóa/di chuyển một số nút, sau đó chỉ cần nhớ rằng bạn phải bảo toàn các yếu tố này và phương pháp này sẽ hoạt động hoàn hảo. Bạn cũng có thể di chuyển các phần tử của dấu trang nếu các thay đổi của bạn cũng sẽ thay đổi lựa chọn.

Đây là cách bạn thực hiện nếu bạn không thể thay thế nội dung của phần tử innerHtml.

Giải pháp này là kém hiệu quả nhưng có thể làm việc trong một số kịch bản

Thay đổi validateText chức năng này.

var validateText = function(editor) { 
    storeCursorLocation(editor); 
    var parent = editor.document.getBody().$.firstChild, 
     nodes = parent.childNodes, 
     nodeText, 
     words, 
     index = 0, 
     current, 
     newElement; 
    while (index < nodes.length) { 
     current = nodes[index]; 
     nodeText = current.nodeValue; 
     if (current.nodeType === Node.TEXT_NODE && nodeText.indexOf('Lorem') !== -1) { 
      words = nodeText.split('Lorem'); 
      newElement = document.createTextNode(words[0]); 
      parent.insertBefore(newElement, current); 
      newElement = document.createTextNode(words[1]); 
      parent.insertBefore(newElement, current.nextSibling); 
      newElement = document.createElement('span') 
      newElement.className = 'err-item'; 
      newElement.innerHTML = 'Lorem'; 
      parent.replaceChild(newElement, current); 
      break; 
     } 
     index++; 

    } 
    restoreCursorLocation(editor); 
}; 

Về cơ bản tôi cơ ngang các nút của p đầu tiên trong cơ thể chkeditor và thay thế duy nhất nút của loại văn bản có chứa Lorem với một nhịp và thêm văn bản còn lại trước và sau khi yếu tố văn bản như. Nếu bạn thay thế toàn bộ văn bản như bạn đang làm, nó sẽ xóa khỏi DOM các dấu trang để khi bạn cố khôi phục chúng không tồn tại.

+0

Trong mã thực Tôi đang sử dụng CKEDITOR.htmlParser để làm điều gì đó tương tự như giải pháp thứ hai của bạn. Nó thực sự tốt hơn là chỉ sử dụng .replace(). Nhưng .replace() là ít phức tạp hơn cho ví dụ, và làm thế nào thay thế xảy ra không phải là vấn đề. –

+1

Với giải pháp đầu tiên bạn có thể sử dụng thay thế. Dấu trang được lưu bằng id và được khôi phục. Tôi đã thêm giải pháp thứ hai bởi vì có một số tình huống trong đó việc sử dụng thay thế khó hơn thay thế một phần tử văn bản đơn lẻ. Đó là một ví dụ như bạn đã nói và cá nhân tôi cố gắng cung cấp thêm dữ liệu để hữu ích hơn với vấn đề thực sự. – devconcept

Các vấn đề liên quan