skip to Main Content

My question is in some ways similar to this post, that post seems very limited compared to what I want to do. I am playing with an idea of having a div that mimics the content editable but better. A user would be able to add text with various formats, styles, fonts, etc. Even including images, videos, and so on. Its nothing more than an idea (so don’t judge it too heavy on its practicality).

In short I have a div that looks like this

        <div tabindex="0" id="canvas">       
          <i id="canvas-cursor">&nbsp</i>
        </div>

the javascript has this

let canvas = document.getElementById("canvas");
let cursor = document.getElementById("canvas-cursor"); 

canvas.addEventListener("keydown", editText);

const removeAttributes = (element) => {
  for (let i = 0; i < element.attributes.length; i++) {
    element.removeAttribute(element.attributes[i].name);
  }
};
function editText(event) {  
  event.preventDefault;
  const keyName = event.key;
  
  if (keyName === "Control") {
    console.log(`Control key ${keyName}`);
    return;
  } 

  if (event.ctrlKey) {
    console.log(`Combination of ctrlKey ${keyName}`);
  } else {

    let newElement = document.createElement("span");
    newElement.setAttribute("class", "canvas-child");
    
    switch (keyName){
    case " ":
      newElement.innerHTML = "&nbsp"; 
      break;
    case "ArrowLeft":
      return;
    case "ArrowRight":
      return;
    case "Backspace":
      var deleted = canvas.removeChild(cursor.previousElementSibling);
      removeAttributes(deleted);
      return;
    case "ArrowUp":
      return; 
    case "ArrowDown":
      return
    case "Enter":
      newElement.innerText += "n"
      break;
    case "Shift": 
    case "Alt":
      console.log("Alt button is pressed.");
    case "Tab":
    case "Meta":
    case "CapsLock":
      return;
 
    default:
      newElement.innerText += keyName
      break;        
    }
    
    newElement.addEventListener("mouseenter", (event)=> {
      // newElement.style.background = "green";
    });

    newElement.addEventListener("mouseleave", (event)=> {
      newElement.style.background = "none";
    });

    newElement.addEventListener("dblclick", (event) => {
      console.log("On click event: " + event
                  + "nsource: " + newElement);
      canvas.insertBefore(cursor, newElement);
    });

    canvas.insertBefore(newElement, cursor); 
  }
}

and css

#canvas-cursor {
    background-color: red;
    animation: blinker 1s linear infinite;
}

Text navigation and font style/formatting has been remove for simplicity, but that works fine; the user can choose code style, underline, bold, font-style, hightlight, etc.

the next two events I want to deal with are copy and paste. I know that I can easily enable that functionality if i add the contenteditable="true" attribute, however, this will also take keyboard inputs and fill the div with text; which means that everything the user types will be repeated; i don’t want this, since I already am taking care of that. I only care to capture the paste event and handle it according to my whims. Is there a way to do this?

thanks in advance.

2

Answers


  1. Chosen as BEST ANSWER

    Ideally I would like for the paste option to appear when right clicking on the div, however this event listener works for the combination Ctl-v, which an approach am okay with.

    canvas.addEventListener('paste', e => {
      const text = e.clipboardData.getData('text/plain')
      // handle text as desired
    })
    

    this link goes into using the system clipboard, which can be another acceptable solution for some I assume.


  2. I guess you want copy and paste in the div and I modified your js code

    let canvas = document.getElementById("canvas");
    let cursor = document.getElementById("canvas-cursor");
    canvas.addEventListener("keydown", editText);
    const removeAttributes = (element) => {
      for (let i = 0; i < element.attributes.length; i++) {
        element.removeAttribute(element.attributes[i].name);
      }
    };
    async function editText(event) {
      event.preventDefault;
      const keyName = event.key;
      if (keyName === "Control") {
        console.log(`Control key ${keyName}`);
        return;
      }
      let newElement = document.createElement("span");
      newElement.setAttribute("class", "canvas-child");
      if (event.ctrlKey) {
        console.log(`Combination of ctrlKey ${keyName}`);
        if (keyName === 'v') {
          try {
            const clipboardItems = await navigator.clipboard.readText();
            if (!clipboardItems) return
            newElement.innerText += clipboardItems
          } catch (err) {
            console.error(err.name, err.message);
          }
        }
      } else {
        switch (keyName) {
          case " ":
            newElement.innerHTML = "&nbsp";
            break;
          case "ArrowLeft":
            return;
          case "ArrowRight":
            return;
          case "Backspace":
            var deleted = canvas.removeChild(cursor.previousElementSibling);
            removeAttributes(deleted);
            return;
          case "ArrowUp":
            return;
          case "ArrowDown":
            return
          case "Enter":
            newElement.innerText += "n"
            break;
          case "Shift":
          case "Alt":
            console.log("Alt button is pressed.");
          case "Tab":
          case "Meta":
          case "CapsLock":
            return;
          default:
            newElement.innerText += keyName
            break;
        }
    
    
      }
      newElement.addEventListener("mouseenter", (event) => {
        // newElement.style.background = "green";
      });
    
      newElement.addEventListener("mouseleave", (event) => {
        newElement.style.background = "none";
      });
    
      newElement.addEventListener("dblclick", (event) => {
        console.log("On click event: " + event +
          "nsource: " + newElement);
        canvas.insertBefore(cursor, newElement);
      });
    
      canvas.insertBefore(newElement, cursor);
    }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search