Make multiple content entries that behave as one document

I have a bunch of multiline content ads arranged vertically, and I want to allow natural navigation between them using the arrow keys (as if it were a single document). For this, in the keydown event keydown I need:

  • Know the current line for the caret and the number of lines to determine whether we need to move up (the first line and the button is pressed) or down (the last line and & darr;)
  • Know the current character (position in the line shown) to determine whether we need to move up (position == 0 and & larr; key pressed) or down (position == text.length and & rarr, pressed)
  • The process should not stop between switching elements when the key is held and not released (therefore, a keydown event, not a keyup )
  • Preferred: the event should stop propagating (for example, if I am in the first column in the last row and I press & darr; key, it should not go to the last character on the line and then go down)
  • Preferred (it would be really awesome): after we move on to the next element, we would not just a .focus() element, but emulate the click in the same vertical position as before, so that it feels natural as in text editors.

All scripts / libraries that I have found to date either do not do everything that I need or do not work. Please include demos in your suggestions so that I can test without first including my code. Thanks!

Update: Visual explanation - note that there are more than two sections, and the down arrow on the last line is only one of four triggers

enter image description here

+6
source share
2 answers

I already wrote some code, but it didn’t finish at all ... Maybe you can start with this and try to complete what I did if you want;) I will continue to work on this this week to ensure you with the solution ... Here is what I have done so far:

 var ajaxResult = [ "Inter has ruinarum varietates a Nisibi quam tuebatur accitus Vrsicinus, cui nos obsecuturos iunxerat imperiale praeceptum, dispicere litis exitialis certamina cogebatur. Inter has ruinarum varietates a Nisibi quam tuebatur accitus Vrsicinus, cui nos obsecuturos iunxerat imperiale praeceptum, dispicere litis exitialis certamina cogebatur. Inter has ruinarum varietates exitialis certamina cogebatur", "Inter has ruinarum varietates a Nisibi quam tuebatur accitus", "Inter has ruinarum varietates a Nisibi quam tuebatur accitus Vrsicinus, cui nos obsecuturos iunxerat imperiale praeceptum, dispicere litis exitialis certamina cogebatur. Inter has ruinarum varietates a Nisibi quamos iunxerat imperiale praeceptum, dispicere litis exitialis certamina cogebatur. Inter has ruinarum varietates exitialis certamina cogebatur", ]; /************************************************************* * * LIST OF CONTENT EDITABLE DIVS MANAGEMENT * **************************************************************/ // Create the editable divs window.onload = function(){ var contentEditables = createContentEditables(); document.body.appendChild(contentEditables); } // Remember all the content editable elements in the order they appear in the dom var _currentEdit, _edits = []; function createContentEditables(){ var div; var result = document.createDocumentFragment(); for (var i = 0, n = ajaxResult.length ; i < n ; i++){ div = createContentEditable(ajaxResult[i]); _edits.push(div); result.appendChild(div); } return result; } function getPreviousEdit(edit){ // Search for the edit index var index = _edits.indexOf(edit); if(index == 0) return; // Return the previous one return _edits[index - 1]; } function getNextEdit(edit){ // Search for the edit index var index = _edits.indexOf(edit); if(index == _edits.length - 1) return; // Return the previous one return _edits[index + 1]; } /************************************************************* * * CONTENT EDITABLE MANAGEMENT * **************************************************************/ // We need to define the line height of the div to be able to retrieve the number of lines var LINE_HEIGHT = 16; // variables to keep trace of relevant information about the div var _lines, _caretPosition; /* * Create a div with contenteditable set to true with the text * received from the server */ function createContentEditable(text){ var element = document.createElement('div'); element.className = 'contenteditable'; element.innerHTML = text; element.style.lineHeight = LINE_HEIGHT + 'px'; element.setAttribute('contenteditable', true); // Set listeners element.addEventListener('mouseup', onEdit_mouseup); element.addEventListener('keydown', onEdit_keydown); element.addEventListener('focus', onEdit_focus); return element; } function onEdit_keydown(domEvent){ // Update caret position _caretPosition = getCaretPosition(domEvent.target); switch(domEvent.keyCode){ case 37: // left arrow if (_caretPosition.index == 0){ var previousEdit = getPreviousEdit(domEvent.target); if(previousEdit){ console.log("go to end of previous edit"); console.log(previousEdit); previousEdit.focus(); } } break; case 38: // up arrow if (_caretPosition.line == 1){ var previousEdit = getPreviousEdit(domEvent.target); if(previousEdit){ console.log("go to previous edit keeping the caret offset"); console.log(previousEdit); previousEdit.focus(); } } break; case 39: // right arrow if (_caretPosition.index == domEvent.target.innerHTML.length){ var nextEdit = getNextEdit(domEvent.target); if(nextEdit){ console.log("go to beginning of next edit"); console.log(nextEdit); nextEdit.focus(); } } break; case 40: // down arrow if (_caretPosition.line == getLines(domEvent.target)){ var nextEdit = getNextEdit(domEvent.target); if(nextEdit){ console.log("go to next edit keeping the caret offset"); console.log(nextEdit); nextEdit.focus(); } } break; } } function onEdit_mouseup(domEvent){ // Update caret position _caretPosition = getCaretPosition(domEvent.target); } function onEdit_focus(domEvent){ // Add listeners _currentEdit = domEvent.target; _currentEdit.addEventListener('blur', onEdit_blur); window.addEventListener('resize', onWindow_resize); } function onEdit_blur(domEvent){ // Remove listeners domEvent.target.removeEventListener('blur', onEdit_blur); window.removeEventListener('resize', onWindow_resize); } function onWindow_resize(domEvent){ // Update caret position _caretPosition = getCaretPosition(_currentEdit); } /************************************************************* * * HELPERS * **************************************************************/ //http://stackoverflow.com/questions/4811822/get-a-ranges-start-and-end-offsets-relative-to-its-parent-container/4812022#4812022 //http://stackoverflow.com/questions/5528004/how-to-get-number-of-rows-in-contenteditable-area-and-current-caret-line-positio function getCaretPosition(element){ var caretPosition = {index: 0, line: 0}; var doc = element.ownerDocument || element.document; var win = doc.defaultView || doc.parentWindow; var elemOffsetTop = element.offsetTop; var sel; // Get the x position of the caret if (typeof win.getSelection != "undefined") { sel = win.getSelection(); if (sel.rangeCount > 0) { var range = win.getSelection().getRangeAt(0); // Retrieve the current line var rects = range.getClientRects(); var caretOffsetTop; if (typeof rects[1] != "undefined"){ caretOffsetTop = rects[1].top; } else if (typeof rects[0] != "undefined"){ caretOffsetTop = rects[0].top; } else{ // Create dummy element to get y position of the caret var dummy = document.createElement('CANVAS'); dummy.id = 'findCaretHelper'; range.insertNode(dummy); caretOffsetTop = dummy.offsetTop; element.removeChild(dummy); } var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); // Remember caret position caretPosition.index = preCaretRange.toString().length; caretPosition.line = Math.ceil((caretOffsetTop - elemOffsetTop)/LINE_HEIGHT) + 1; } } // support ie //else if ( (sel = doc.selection) && sel.type != "Control") { //var textRange = sel.createRange(); //var preCaretTextRange = doc.body.createTextRange(); //preCaretTextRange.moveToElementText(element); //preCaretTextRange.setEndPoint("EndToEnd", textRange); //caretPosition.x = preCaretTextRange.text.length; //} return caretPosition; } function getLines(element){ return element.clientHeight/LINE_HEIGHT;; } 
 .contenteditable{ border: solid 1px #aaa; margin: 10px 0; } 

I managed to get information about the current line, the current index of characters in the editable div content and some other materials ... I still need to work on focusing other content, the editable div to put the caret in the right place ... I hope this is the beginning solutions will help you!

+3
source

You can simply create a parent / containing contenteditable element as opposed to each paragraph. This will automatically add / remove p tags.

https://jsfiddle.net/ewesmwmv/2/

0
source

All Articles