我正在尝试构建一个页面构建器,您输入html,它呈现为div进行预览,您可以编辑div(而不是html),它将相应地更新textarea。我遇到的问题是当我试图将文本从div移回文本区域时,某些文本不在包含标记之外。
我已经制作了一个JSFiddle来向您展示问题所在:
http://jsfiddle.net/7crysb0L/4/
Jquery: -
$('#dohtml').click(function () {
var textAreaVal = $('#myTextArea').val();
$('#myDiv').html(textAreaVal);
});
$('#dotext').click(function () {
var $div = $('#myDiv'),
isEditable = $div.is('.editable');
$('#myDiv').prop('contenteditable', !isEditable).toggleClass('editable')
});
$('#dosave').click(function () {
var test = $('#myDiv').html();
alert(test);
$('#myTextArea').val(test);
});
HTML: -
<div id="container">
<textarea id="textinput" placeholder="HTML here"></textarea>
<br/>
<button class="btn btn-primary" id="convert">View</button>
<button class="btn btn-primary" id="edit">Edit Div</button>
<button class="btn btn-primary" id="save">Save Changes</button>
<div id="htmloutput">
</div>
</div>
例如,如果您复制并粘贴此处的引导手风琴:
http://www.tutorialspark.com/twitterBootstrap/TwitterBootstrap_Collapsible_Accordion.php
并编辑div以更改ahref,如果您在结尾处键入,或者在开头或删除太多,则整个div消失。有没有解决这个问题?
我很确定我知道它为什么会发生: -
当删除div的最后一个字符时,div会自动删除,这会打破div中的html。
答案 0 :(得分:0)
这个小提琴为您的问题提供了另一种选择。它不会使加载的HTML可编辑,但允许用户从中选择元素并更改该元素的属性。您可以通过允许插入新属性,删除属性,重写innerHTML等来大大扩展它。当您遍历树到a
元素时,您可以调整href而不会破坏元素的结构。
代码:
使用Javascript:
function recursiveHTMLTraverser(html, el, trace)
{
var ulNode = document.createElement("ul");
var html = html.children;
var elementArray = [];
for( var i = 0; i < html.length; ++i)
{
if (!elementArray[html[i].nodeName])
{
elementArray[html[i].nodeName] = [0];
}
else
{
elementArray[html[i].nodeName].push(elementArray[html[i].nodeName].length);
}
var liNode = document.createElement("li");
var name = "";
if (html[i].getAttribute("class") || html[i].getAttribute("id") || html[i].getAttribute("name"))
{
name = " (<span>";
if (html[i].getAttribute("id"))
{
name += html[i].getAttribute("id") + "</span>)";
}
else if (html[i].getAttribute("class"))
{
name += html[i].getAttribute("class") + "</span>)";
}
else
{
name += html[i].getAttribute("name") + "</span>)";
}
}
var expander = "";
if (html[i].children.length > 0)
{
expander = "<span class='expander'>+</span>";
}
liNode.setAttribute("data-node", html[i].nodeName);
liNode.setAttribute("data-trace", trace + html[i].nodeName);
liNode.setAttribute("data-index", elementArray[html[i].nodeName].length-1);
liNode.innerHTML = "<div>" + expander + html[i].nodeName + name + "</div>";
ulNode.appendChild(liNode);
if (html[i].children.length > 0)
{
recursiveHTMLTraverser(html[i], liNode, trace + html[i].nodeName + " | ");
}
}
if (ulNode.children.length > 0)
{
el.appendChild(ulNode);
}
}
function loadHTMLNode(e)
{
if (e.target && e.target.tagName)
{
if (e.target.tagName.toLowerCase == "li")
{
var source = e.target;
}
else if (e.target.className && e.target.className == "expander")
{
if (e.target.textContent == "+")
{
e.target.textContent = "-";
e.target.parentElement.parentElement.querySelector("ul").style.display = "block";
}
else
{
e.target.textContent = "+";
e.target.parentElement.parentElement.querySelector("ul").style.display = "none";
}
return true;
}
else
{
var source = e.target;
while(source = source.parentElement)
{
if (source.tagName.toLowerCase() == "li")
{
break;
}
}
}
var selector = source.getAttribute("data-trace").replace(/\|/g, ">");
document.getElementById("attributeEditor").removeAttribute("data-selector");
document.getElementById("attributeEditor").removeAttribute("data-index-element");
document.getElementById("attributeEditor").removeAttribute("data-index-attribute");
document.getElementById("innerHTMLEditor").value = documentIframe.querySelectorAll(selector)[source.getAttribute("data-index")].innerHTML;
loadHTMLNodeAttributes(selector, source.getAttribute("data-index"));
}
}
function loadHTMLNodeAttributes(selector, index)
{
var element = documentIframe.querySelectorAll(selector)[index];
var selectElement = document.getElementById("attributeSelector").cloneNode();
document.getElementById("attributeSelector").removeEventListener("change", changeAttribute(selector, index), false);
for (var i = 0; i < element.attributes.length; ++i)
{
var optionNode = document.createElement("option");
optionNode.value = i;
optionNode.textContent = element.attributes[i].name;
selectElement.appendChild(optionNode);
}
document.getElementById("attributeEditor").value = "";
if (element.attributes.length > 0)
{
selectElement.addEventListener("change", changeAttribute(selector, index), false);
var starter = changeAttribute(selector, index); //mimic call to change function;
starter.call({value : 0}); //insert object with value 0, to select the first element.
}
document.getElementById("attributeSelector").parentElement.replaceChild(selectElement, document.getElementById("attributeSelector"));
}
function changeAttribute(selector, index)
{
return function(){
document.getElementById("attributeEditor").value = documentIframe.querySelectorAll(selector)[index].attributes[this.value].value;
document.getElementById("attributeEditor").setAttribute("data-selector", selector);
document.getElementById("attributeEditor").setAttribute("data-index-element", index);
document.getElementById("attributeEditor").setAttribute("data-index-attribute", this.value);
}
}
function changeAttributeValue(e)
{
//change the attribute real life.
if (e.target.hasAttribute("data-selector") && e.target.hasAttribute("data-index-element") && e.target.hasAttribute("data-index-attribute"))
{
documentIframe.querySelectorAll(e.target.getAttribute("data-selector"))[e.target.getAttribute("data-index-element")].attributes[e.target.getAttribute("data-index-attribute")].value = document.getElementById("attributeEditor").value;
}
}
function startHTMLPreview()
{
documentIframe.write(document.getElementById("myTextArea").value);
recursiveHTMLTraverser(documentIframe.querySelector("html"), document.getElementById("domTreeDiv"), "");
}
document.getElementById("dohtml").addEventListener("click", startHTMLPreview, false);
document.getElementById("domTreeDiv").addEventListener("click", loadHTMLNode, false);
document.getElementById("attributeEditor").addEventListener("keyup", changeAttributeValue, false);
documentIframe = document.getElementById("myDiv").contentDocument.document || document.getElementById("myDiv").contentDocument;
HTML:
<textarea id="myTextArea" placeholder="Type here, then click the button to update the div">
</textarea>
<br />
<button id="dohtml">Render HTML</button>
<br /><br />
Element tree:
<div id="domTreeDiv"></div>
<div id="domTreeEditor">
Attributes:
<select id="attributeSelector"></select> = <input id="attributeEditor"><br />
innerHTML: <textarea id="innerHTMLEditor" ></textarea>
</div>
Preview: <br />
<iframe id="myDiv" seamless sandbox="allow-same-origin"></iframe>
CSS:
body{
font-size: 11px;
font-family: verdana;
}
#myDiv {
border:solid 1px #999;
border-radius:4px;
min-height:300px;
min-width:600px;
margin-top:1em;
padding:0.5em;
}
#myTextArea {
min-width:600px;
height: 200px;
}
#domTreeDiv {
border: 1px solid #e2e2e2;
border-radius: 3px 3px 3px 3px;
min-height: 50px;
max-height: 450px;
}
#domTreeDiv ul {
list-style-type: none;
padding: 0px 0px 0px 20px;
}
#domTreeDiv li > ul {
display: none;
}
#domTreeDiv ul li > div {
padding: 8px 8px 8px 8px;
cursor: pointer;
border: 1px solid transparent;
}
#domTreeDiv ul li > div:before {
content:"";
}
#domTreeDiv li > div:hover {
padding: 8px 8px 8px 8px;
cursor: pointer;
border: 1px solid #BFDFFF;
background-color: #99CCFF;
}
#domTreeDiv li > div > span {
color: #8C0000;
}
#domTreeDiv li > div > span.expander {
color: #000000;
display: inline-block;
width: 24px;
height: 100%;
font-weight: bold;
}
#domTreeDiv li > div > span.expander:hover {
color: #EEEEEE;
}
#attributeEditor{
width: 400px;
padding: 8px 8px 8px 8px;
margin: 10px 0px 10px 0px;
}
#attributeSelector{
width: 100px;
padding: 8px 8px 8px 8px;
}