首页 > 解决方案 > textarea中的多个单独区域选择?

问题描述

我对编码很陌生,并且一直在使用 textarea 制作文本编辑器组件:https ://jsfiddle.net/chrismg12/jLk70ar3/5/

我打算创建一个函数来选择找到的单词的所有实例。但是我不知道如何在 textarea 中这样做。注意:这不是关于多个插入符号,而是多个选择,但是在实现多个插入符号时的建议将不胜感激。
HTML

var lNo = "";

function injectStyles(rule) {
  var div = $("<div />", {
    html: '&shy;<style>' + rule + '</style>'
  }).appendTo("body");
}

$.fn.selectRange = function(start, end) {
  return this.each(function() {
    if (typeof end == "undefined") {
      end = start;
    }
    if (start == -1) {
      start = this.value.length;
    }
    if (end == -1) {
      end = this.value.length;
    }
    if (this.setSelectionRange) {
      this.focus();
      this.setSelectionRange(start, end + 1);
    } else if (this.createTextRange) {
      var range = this.createTextRange();
      range.collapse(true);
      range.moveEnd('character', end);
      range.moveStart('character', start);
      range.select();
    }
  });
};

$.fn.selectRangeExcludingLast = function(start, end) {
  return this.each(function() {
    if (typeof end == "undefined") {
      end = start;
    }
    if (start == -1) {
      start = this.value.length;
    }
    if (end == -1) {
      end = this.value.length;
    }
    if (this.setSelectionRange) {
      this.focus();
      this.setSelectionRange(start, end + 1);
    } else if (this.createTextRange) {
      var range = this.createTextRange();
      range.collapse(true);
      range.moveEnd('character', end);
      range.moveStart('character', start);
      range.select();
    }
  });
};

function getPos(str, substr, index) {
  var io = str.split(substr, index).join(substr).length;
  if (io >= str.length) {
    return -1;
  } else {
    return io;
  }
}

function gutterChildControl(gutter, textarea, settings) {
  if (gutter.childNodes.length != $(textarea).val().split("\n").length) {
    while (gutter.childNodes.length < $(textarea).val().split("\n").length) {
      var gutterChild = document.createElement("div");
      gutterChild.className = "gutterChild"
      gutterChild.style.height = $(textarea).css('line-height');
      var gutterChildLineHeight = $(textarea).css('line-height');
      $(gutterChild).css('line-height', gutterChildLineHeight)
      gutterChild.style.fontSize = $(textarea).css('font-size');
      gutterChild.style.width = "100%";
      gutterChild.style.color = settings.gFontCol;
      gutterChild.style.paddingRight = `${settings.gPadding}px`;
      gutterChild.style.paddingLeft = `${settings.gPadding}px`;
      gutterChild.style.textAlign = settings.gFontAlign;
      gutterChild.onmouseover = function() {
        gutterChild.style.opacity = "0.75"
      }
      gutterChild.onmouseleave = function() {
        gutterChild.style.opacity = "1"
      }
      gutterChild.onclick = function() {
        if (parseInt(gutterChild.innerHTML) != 1) {
          startPos = getPos($(textarea).val(), "\n", parseInt(gutterChild.innerHTML) - 1)
          console.log(`startPos:${startPos}`);
          endPos = getPos($(textarea).val(), "\n", parseInt(gutterChild.innerHTML))
          if (endPos == -1) {
            endPos = $(textarea).val().length - 1
          }
          console.log(`endPos:${endPos}`)
          $(textarea).selectRange(startPos, endPos);
        } else {
          startPos = 0;
          console.log(`startPos:${startPos}`)
          endPos = getPos($(textarea).val(), "\n", 1)
          if (endPos == -1) {
            endPos = $(textarea).val().length - 1
            if (endPos == -1) {
              endPos = 0;
            }
          }
          console.log(`endPos:${endPos}`)
          $(textarea).selectRange(startPos, endPos);
        }
      }

      gutter.appendChild(gutterChild);
      gutterChild.innerHTML = `${gutter.childNodes.length}`
    }
    while (gutter.childNodes.length > $(textarea).val().split("\n").length) {
      gutter.removeChild(gutter.lastChild)
    }
    // while(gutter.childNodes.length > $(textarea).val().split("\n").length){
    //  gutter.childNodes.pop;
    // }
  }
}

function Ventify(element, options) {
  const settings = {
    taCol: "#1d252c",
    gCol: "#1d252c",
    fSize: "22px",
    gFontCol: "rgba(58,74,88,1)",
    pad: 1,
    gFontAlign: "center",
    gPadding: 20,
    ...options
  }
  element.style.display = "flex";
  element.style.flexDirection = "row";
  var gutter = document.createElement("div");
  var textarea = document.createElement("textarea");
  gutter.className = "gutter";
  gutter.classList.add("scrll")
  textarea.className = "ventiEditor";
  textarea.classList.add("scrll")

  gutter.style.width = "100px";
  gutter.style.height = "100%";
  gutter.style.backgroundColor = settings.gCol;
  gutter.style.overflowY = "scroll"
  gutter.style.scrollbarWidth = "none";
  injectStyles(".gutter::-webkit-scrollbar{width:0px;}")
  injectStyles(".ventiEditor{outline:0px !important;-webkit-appearance:none;}")

  textarea.style.width = "calc(100% - 100px)";
  textarea.style.fontSize = settings.fSize;
  textarea.style.color = settings.gFontCol;
  textarea.style.overflowY = "scroll";
  textarea.style.whiteSpace = "pre";
  textarea.style.resize = "none";
  textarea.style.height = "100%";
  textarea.style.margin = "0px 0px 0px 0px"
  textarea.style.border = "0px solid rgb(255,255,255)"
  textarea.style.padding = "0"
  $(textarea).css('line-height', `calc(${2*settings.pad}px + ${settings.fSize})`)
  textarea.style.backgroundColor = settings.taCol;
  textarea.spellcheck = false;
  $(textarea).css("-moz-tab-size", "4");
  $(textarea).css("tab-size", "4")
  $('.ventiEditor:focus').css('outline', '0px !important')
  $('.ventiEditor:focus').css('-webkit-appearance', 'none')
  textarea.oninput = function() {
    var gutter = textarea.parentElement.childNodes[0];
    gutterChildControl(gutter, textarea, settings)
  };
  textarea.onscroll = function() {
    gutter.scrollTop = textarea.scrollTop
  }
  $(textarea).keydown(function(e1) {
    if (e1.ctrlKey) {
      if (e1.keyCode >= 48 && e1.keyCode <= 57) {
        lNo += e1.key;
        console.log(lNo);
      }
    }
  })
  // gutter.onscroll = function(){
  //  if(gutter.scrollTop != textarea.scrollTop){
  //      textarea.scrollTop = gutter.scrollTop;
  //  }
  // }
  element.appendChild(gutter);
  element.appendChild(textarea);
  gutterChildControl(gutter, textarea, settings);
}

function gutterFix(textarea) {}

$(document).delegate('.ventiEditor', 'keydown', function(e) {
  var keyCode = e.keyCode || e.which;

  if (keyCode == 9) {
    e.preventDefault();
    var start = this.selectionStart;
    var end = this.selectionEnd;

    // set textarea value to: text before caret + tab + text after caret
    $(this).val($(this).val().substring(0, start) +
      "\t" +
      $(this).val().substring(end));

    // put caret at right position again
    this.selectionStart =
      this.selectionEnd = start + 1;
  }
});

injectStyles(".gutterChild{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}")
Ventify(document.getElementsByClassName("container")[0], {
  taCol: "#1d252c",
  gCol: "#1d252c",
  pad: 5,
  gFontCol: '#bbb'
})
html,
body {
  height: 100%;
  margin: 0px 0px 0px 0px;
}

.container {
  height: 100%;
}
<link rel="stylesheet" href="bootstrap-4.3.1-dist/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container"></div>

标签: javascriptjqueryhtml

解决方案


您可以设置一个空数组并将所有匹配字符串的位置推送到它。this(例如,如果您在字符串 ' ' 中寻找' this and this match; so does this',您的数组将得到 [0,9,29]。)您可能想要/需要使用正则表达式

有了这些信息(并知道子字符串的长度),您可以执行以下操作:
- 在 textarea 上覆盖一个 div(可能使用contenteditable="true"z-index: 1
- 将每个匹配的字符串包含在一个 span 元素中(更多信息关于这里)并且可能给所有这些跨度一个类,
- 使用 querySelectorAll 来获取一个包含所有这些跨度的 NodeList,并且
- 循环遍历列表以更改它们的样式(以便用户可以看到什么是“选定的”)和它们的内容(假设重点是让用户能够进行更改。)

您可以稍后删除环绕跨度,使用更新的文本更新文本区域,并删除覆盖的 div。
请注意,您的索引数组可能不会长时间保持有效,并且根据您对初始列表所做的操作,您可能希望向后循环以保护这些值,直到您不再需要它们为止。

无论如何,这是我的头脑风暴。


推荐阅读