Skip to content Skip to sidebar Skip to footer

Undo Is Working For Only First Replaced Text?

Here I am using Ctrl+Z for undoing the replaced text, I have a scenario that, in text-area, I have a sentence with multiple words, where I select first word and replaced with stars

Solution 1:

You have to manually control all your textarea changes.

Here I created the edits array which is populated with textarea text on change every few seconds (you can control it using the saveInterval variable). You also can set the max length of this array using maxHistorySize. When the array is full, the old changes are lost.

var edits = [""];
var interval = true;
var maxHistorySize = 10;
var saveInterval = 3000;

functionundo(e) {
      var evtobj = window.event? window.event : e;
      if (evtobj.keyCode == 90 && evtobj.ctrlKey) {
          evtobj.preventDefault();
          var txtarea = document.getElementById("mytextarea");
          var previousText = edits.length === 1 ? edits[0] : edits.pop();
          if (previousText !== undefined) {
              txtarea.value = previousText;
          }
      }
}

functiongetSel() {
    // obtain the object reference for the textarea>var txtarea = document.getElementById("mytextarea");
    // obtain the index of the first selected charactervar start = txtarea.selectionStart;
    // obtain the index of the last selected charactervar finish = txtarea.selectionEnd;
    //obtain all Textvar allText = txtarea.value;
    
    edits.push(allText);
    if (edits.length > maxHistorySize) edits.shift();

    // obtain the selected textvar sel = Array(finish - start + 1).join("*");
    //append te text;var newText = allText.substring(0, start) + sel + allText.substring(finish, allText.length);
    txtarea.value = newText;
    
    $('#newpost').offset({top: 0, left: 0}).hide();
}
functionclosePopUp() {
    $('#newpost').offset({top: 0, left: 0}).hide();
}

$(document).ready(function () {
    closePopUp();
    var newpost = $('#newpost');
    $('#mytextarea').on('select', function (e) {
        var txtarea = document.getElementById("mytextarea");
        var start = txtarea.selectionStart;
        var finish = txtarea.selectionEnd;
        newpost.offset(getCursorXY(txtarea, start, 20)).show();
        newpost.find('div').text(Array(finish - start + 1).join("*"));
    }).on('input', function() {
        if (interval) {
            interval = false;
            edits.push($(this).val());
            if (edits.length > maxHistorySize) edits.shift();
            setTimeout(() => interval = true, saveInterval);
        }
    });
    document.onkeydown = undo;
});

constgetCursorXY = (input, selectionPoint, offset) => {
  const {
    offsetLeft: inputX,
    offsetTop: inputY,
  } = input
  // create a dummy element that will be a clone of our inputconst div = document.createElement('div')
  // get the computed style of the input and clone it onto the dummy elementconst copyStyle = getComputedStyle(input)
  for (const prop of copyStyle) {
    div.style[prop] = copyStyle[prop]
  }
  // we need a character that will replace whitespace when filling our dummy element // if it's a single line <input/>const swap = '.'const inputValue = input.tagName === 'INPUT' ? input.value.replace(/ /g, swap) : input.value// set the div content to that of the textarea up until selectionconst textContent = inputValue.substr(0, selectionPoint)
  // set the text content of the dummy element div
  div.textContent = textContent
  if (input.tagName === 'TEXTAREA') div.style.height = 'auto'// if a single line input then the div needs to be single line and not break out like a text areaif (input.tagName === 'INPUT') div.style.width = 'auto'// create a marker element to obtain caret positionconst span = document.createElement('span')
  // give the span the textContent of remaining content so that the recreated dummy element // is as close as possible
  span.textContent = inputValue.substr(selectionPoint) || '.'// append the span marker to the div
  div.appendChild(span)
  // append the dummy element to the bodydocument.body.appendChild(div)
  // get the marker position, this is the caret position top and left relative to the inputconst { offsetLeft: spanX, offsetTop: spanY } = span
  // lastly, remove that dummy element// NOTE:: can comment this out for debugging purposes if you want to see where that span is rendereddocument.body.removeChild(div)
  // return an object with the x and y of the caret. account for input positioning // so that you don't need to wrap the inputreturn {
    left: inputX + spanX,
    top: inputY + spanY + offset,
  }
}
#mytextarea {width: 600px; height: 200px; overflow:hidden; position:fixed}
#newpost {
    position:absolute;
    background-color:#ffffdc;
    border:1px solid #DCDCDC;
    border-radius:10px;
    padding-right:5px; 
    width: auto;
    height: 30px;
}
#newpostspan {
    cursor:pointer;
    position: absolute;
    top: 0;
    right: 5px;
    font-size: 22px;
}
#newpostdiv {
    color:#0000ff;
    padding:10px;
    margin-right:10px;
    width: auto;
    cursor:pointer;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><html><head></head><body><textAreaid="mytextarea"></textArea><divid="newpost"><spanonclick="closePopUp();">&#735;</span><divonclick="getSel()"></div></div></body></html>

Solution 2:

You need to change your logic from using a single selection object to using an array of selection objects, like this:

  1. Initialize to selections instead of selection

    var selections = [];
    
  2. When getSel() is called, make sure you update to push to array:

    functiongetSel() {
        // obtain the object reference for the textarea>var txtarea = document.getElementById("mytextarea");
        // obtain the index of the first selected charactervar start = txtarea.selectionStart;
        // obtain the index of the last selected charactervar finish = txtarea.selectionEnd;
        //obtain all Textvar allText = txtarea.value;
    
        selections.push({
          start: start,
          finish: finish,
          text: allText.substring(start, finish)
        });
        // obtain the selected textvar sel = allText.substring(start, finish);
        sel = sel.replace(/[\S]/g, "*"); //append te text;var newText = allText.substring(0, start) + sel + allText.substring(finish, allText.length);
        txtarea.value = newText;
    
        $('#newpost').offset({top: 0, left: 0}).hide();
    }
    
  3. As such, we need to update undo function, as follows:

    functionundo(e) {
        var evtobj = window.event? window.event : e;
    
        if (evtobj.keyCode == 90 && evtobj.ctrlKey) {
            evtobj.preventDefault();
    
            if (selections.length === 0) alert ("Can't do more undos");
            else {
                var thisSelection = selections.pop();
                var txtarea = document.getElementById("mytextarea");
                var allText = txtarea.value;
                var newText = allText.substring(0, thisSelection.start) + thisSelection.text + allText.substring(thisSelection.finish, allText.length);
                txtarea.value = newText;
            }
        }
    }
    
  4. Lastly, remove the on('input') event listener from $('#mytextarea') as I don't think it's needed

Here's the plunk with all the changes

Post a Comment for "Undo Is Working For Only First Replaced Text?"