【问题标题】:Vanilla JavaScript: How to dynamically create a button that shows up after user has selected some text and then do something with that text?Vanilla JavaScript:如何动态创建一个在用户选择一些文本后显示的按钮,然后对该文本执行某些操作?
【发布时间】:2022-01-22 20:11:31
【问题描述】:

我正在开发一个小项目,用户可以在该项目中上传他们的 .docx 文件,然后直接在应用程序中读取这些文件(以及其他用户的文件)。为此,我从 docx 中提取了文本并将其显示在单独的视图和 html 页面中的 div 中。现在,我想给我的用户一个选项来从这个 div(并且只有这个 div)中选择一些文本,当他们选择它时,我想显示一个悬停在他们可以点击的文本上的按钮。这个按钮用于将选定的文本添加到他们的笔记中,类似于我们选择一些文本时 MS Edge 自动执行的操作(它添加这三个点,然后打开一个用于复制的菜单和其余部分)。甚至更确切地说,如果您熟悉Coursera网站 - 选择来自课程的一些文本,则会出现一个“保存注意”按钮,然后单击它在您的笔记中添加所选文本,而无需用户可以执行其他任何操作,并且他们可以在查看笔记时找到已保存的笔记(下图显示了此按钮的运行情况)。

Coursera 'Save Note' button example

但是,我不确定如何实现这一点。我想我会使用 window.getSelection,然后将选择存储在某个 const 中,然后我会通过 fetch 将其发送到我的服务器以将其添加到 Notes 模型(我正在使用 Django)。但我不知道如何实现这一点,也不知道从哪里开始。仅在选择选择时,如何使按钮悬停在这样的其他文本上?非常感谢任何形式的帮助!注意:如果可能的话,我希望在 Vanilla JS 中这样做,因为我还不熟悉 React 或其他库/框架。

【问题讨论】:

    标签: javascript html css button


    【解决方案1】:

    将问题分解为任务:

    1. 检测选择开始和结束 - 可以分别使用onmouseuponmousedown 完成。
    • 使用EventHandler可用的事件对象查找所选区域的坐标

    • onmousedown存储选择开始的坐标

    • onmouseup获取选择结束的坐标

    • 使用开始和结束坐标来定位复制按钮(使用CSS

    1. 获取选定文本 - window.getSelection 可用于在按下按钮时获取选定文本。

    一旦检测到选择并且选择的文本可用,就可以将其推送到服务器。

    类似这样的:

    const copyBtn = document.createElement("button");
    copyBtn.innerHTML = "Press this button to log selected text";
    copyBtn.style.position = "absolute";
    copyBtn.style.display = "none";
    copyBtn.onclick = function(evt) {
      console.log(window.getSelection().toString())
    }
    document.body.appendChild(copyBtn);
    
    const paragraph = document.querySelector('p');
    let startX = 0;
    let startY = 0;
    
    function mouseDown(evt) {
      console.log(`Selection started at : ${evt.clientX}, ${evt.clientY}`);
      startX = evt.clientX;
      startY = evt.clientY;
    
      copyBtn.style.display = "none";
    }
    
    function mouseUp(evt) {
      console.log(`Selection ended at : ${evt.clientX}, ${evt.clientY}`);
    
      copyBtn.style.display = "block";
      copyBtn.style.top = `${Math.min(startY, evt.clientY)}px`;
      copyBtn.style.left = `${(startX + evt.clientX) / 2}px`;
    
    }
    paragraph.onmouseup = mouseUp;
    paragraph.onmousedown = mouseDown
    <!DOCTYPE html>
    <html>
    <body>
    
    <p>
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
    </p>
    
    </body>
    </html>

    【讨论】:

    【解决方案2】:

    JavaScript 中有一个函数可以检索当前选定的文本:

    function getSelectionText() {
        if (document.getSelection().toString().length === 0) {
    //do nothing
        } else if (document.getSelection().toString().length > 0) {
            //fire button function
        }
        return text;
    }
    

    我相信没有一个函数只在选择文本时触发,所以该函数应该不断运行,或者当有一个 dom "mouseup" 事件时。

    【讨论】:

      【解决方案3】:

      必须回答我自己的问题,以供将来参考。 感谢 Tyler Durden 和 Endoxos 的解决方案,在玩了几个小时之后,现在(大部分情况下)这是我想要它做的代码(它也被注释掉了答案以便于理解):

      /* Read view - dynamically adding Save Note button after selection of text */
      document.addEventListener('DOMContentLoaded', function() {
          /* Use this functions only in a div that contains displayed contents of files */
          const content = document.querySelector('#docContent');
          /* Create and append button */
          const noteBtn = document.createElement('button');
          noteBtn.innerHTML = 'Save Note';
          noteBtn.style.position = 'absolute';
          noteBtn.style.display = 'none';
          noteBtn.className = 'btn btn-sm btn-danger';
      
          content.appendChild(noteBtn);
      
          let startX = 0;
          let startY = 0;
      
          /* On mousedown only save starting X and Y, but relevant to entire page, 
           not the client X and Y, which causes button to stay on top part of doc,
           even if we want to select text from bottom part. */
          content.addEventListener('mousedown', function(evt){
              startX = evt.pageX;
              startY = evt.pageY;
          });
          
          /* On mouse up, we check if the end X and Y differ from starting
          and if, we place the button to the end of the selection, where user's
          mouse will naturally be, after making the selection. This works on every
          part of the page and dom, except on the far right side (if selection ends
          on the endpoint on right side, that is), and for these cases one might make
          calculations and for those cases just reverse the direction of button, but
          I can't be bothered to do so today, maybe tomorrow... Also, if the start and
          end X and Y do not differ, most likely user wanted to click somewhere to 'hide'
          the popped up button, so we just set its display to none in such case*/
          content.addEventListener('mouseup', function(evt) {  
              if (evt.pageX != startX && evt.pageY != startY ) {
                  noteBtn.style.top = `${evt.pageY}px`;
                  noteBtn.style.left = `${evt.pageX}px`;
                  noteBtn.style.display = 'block';
              } else {
                  noteBtn.style.display = 'none';
              }
          });
      
          /* Finally, we add event listener for clicks on button, and when the button is
          clicked we save the text to const, and pass that to our view in Django (in this 
          case there is csrf_exempt, but normally one would do that too...) */
          noteBtn.addEventListener('click', function() {
              const note = document.getSelection().toString();
              const id = content.querySelector('.reading_content_id').value;
              fetch(`/add_note/${id}`, {
                  method: 'POST',
                  body: JSON.stringify({
                      note:`${note}`
                  })
              }).then (function() {
                  document.getSelection().collapseToEnd();
                  noteBtn.style.display = 'none';
              });
          });
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-02
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多