Cannicide
Cannicide

Reputation: 4520

HTML Elements inside Contenteditable?

I have a contenteditable tag, and I want my users to be able to type code into it. However, when I type into the contenteditable tag, my code shows up as text rather than an actual element. Is there a way for a user to create a full, working HTML element in a contenteditable box? I know it is possible for the client to insert code using javascript, but what about users who do not have access to javascript? How could users get code such as buttons inside a contenteditable box?

<p contenteditable="true">Try typing code in here as user, code will only be text...</p>

Is there a javascript way to accomplish this without JQUERY?

EDIT

I spent a long time searching for answers on Google, but nothing came up. The best solution I've gotten at this point has been @Dekel's comment on CKEditor. If there is another solution, I want to hear it. If there isn't, I'm sticking to CKEditor. I don't have much time, so I need a solution fast.

MORE EDIT =D

I recently developed my own answer to my question by looking at @Brandon's .replace answer (which only worked for client-coding, not user-coding) and modifying it to work with user-coding.

Upvotes: 5

Views: 5467

Answers (3)

Gabriel
Gabriel

Reputation: 2190

You can't insert code, but you can insert DOMElements with JS. No need for jQuery.

var element=document.createElement("button");
element.innerHTML="Hello";
document.getElementById("yourContentEditable").append(element);

The idea with this would be to have a button to prompt for the code and insert it. Something like this:

(It is very ugly and buggy but it's just an example I just wrote)

var editorSelection=null;

function openCodePopup() {
  //Store cursor position before editor loses focus
  editorSelection=getEditorSelection();
  
  //Open the popup
  document.querySelector("#codePopup").style.display="block";
  var ta=document.querySelector("#userCode");
  ta.value="";
  ta.focus();
}

function closeCodePopup() {
  document.querySelector("#codePopup").style.display="none";
}

function insertCode() {
  var code=document.querySelector("#userCode").value;
  closeCodePopup();
  if(code=="") return;
  insertIntoEditor(html2dom(code));
}

function getEditorSelection() {
  //TODO make crossbrowser
  //TODO (VERY IMPORTANT) validate if selection is whitin the editor
  var sel=window.getSelection();
  if(sel.rangeCount) return sel.getRangeAt(0);
  return null;
}

function insertIntoEditor(dom) {
  if(editorSelection) {
    editorSelection.deleteContents();
    editorSelection.insertNode(dom);
  } else {
    //Insert at the end
    document.querySelector("#editor").append(dom);
  }
}

function html2dom(code) {
  //A lazy way to convert html to DOMElements, you can use jQuery or any other 
  var foo=document.createElement('div'); //or you could use an inline element
  foo.contentEditable=false;
  foo.innerHTML=code;
  return foo;
}
#editor {
  height: 180px;
  overflow: auto;
  border: 1px solid #ccc;
}
#toolbar {
  position: relative;
}
#codePopup {
  position: absolute;
  left: 10px;
  top: 15px;
  background-color: #fff;
  border: 1px solid #ccc;
  padding: 5px;
  display: none;
}
#userCode {
  display: block;
  width: 200px;
  height: 100px;
}
<div id="toolbar">
  <button onclick="openCodePopup()">&lt;/&gt;</button>
  <div id="codePopup">
    <textarea id="userCode" placeholder="Type code here"></textarea>
    <button onclick="insertCode()">Ok</button>
    <button onclick="closeCodePopup()">Cancel</button>
  </div>
</div>
<div contenteditable="true" id="editor"></div>

With the same idea you could create other options to convert element (example, text->link, etc.).

Upvotes: 2

Cannicide
Cannicide

Reputation: 4520

This answer is similar to @Brandon's idea, but is much more simple.

https://jsfiddle.net/azopqLe4/

<iframe width="100%" height="300" src="//jsfiddle.net/azopqLe4/embedded/js,html,result/dark/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>

function convertit() {
	var convet = document.getElementById("convet");
  var text = convet.innerHTML;
  var newtext;

  newtext = text.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
  
  convet.innerHTML = newtext;
}
//this version runs onrightclick =D
<p contenteditable="true" oncontextmenu="convertit();" id="convet">
Type some code here, then right-click... =D
</p>
In the second snippet, I typed <b>Test</b>, right-clicked it, and it became Test! My answer works through simple array replacement methods, although it is frustrating and time-wasting to keep right-clicking all the time. To prevent the actual contextmenu from popping up, just add .preventDefault().

Upvotes: 2

Brandon
Brandon

Reputation: 200

This isn't pretty, but you could make it work if you are looking to add HTML only. Otherwise an inline editor might work best.

var el = document.querySelector('p')
el.addEventListener('blur', function() {
  var map = {amp: '&', lt: '<', gt: '>', quot: '"', '#039': "'"}
  var html = this.innerHTML.replace(/&([^;]+);/g, (m, c) => map[c]);
  this.innerHTML = html;
});
<p contenteditable="true">Try typing <b>code</b> in here as user, code will only be text...</p>

Upvotes: 5

Related Questions