我正在制作一个最小的 JavaScript 所见即所得控件。
我不想使用 document.execCommand,因为它不允许任意 HTML,跨浏览器不一致等等。
这是我迄今为止精简到最少的工作代码:
<button data-action="strong"><strong>b</strong></button>
<button data-action="em"><em>i</em></button>
<button data-action="u"><u>u</u></button>
<p contenteditable>The quick brown fox jumps over the lazy dog.</p>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$( function(){
$( 'button' ).on( 'click', function(){
var selection = window.getSelection();
var range = selection.getRangeAt( 0 );
var action = $( this ).attr( 'data-action' );
var node = document.createElement( action );
var frag = range.extractContents();
node.appendChild( frag );
range.insertNode( node );
return false;
});
});
</script>
如果某些选区已经包含强标签(或其他),我该如何做到第二次单击按钮会删除这些标签,而不是用新的强标签包装选区?
写这个问题给了我一个想法。我现在会尝试并回答我自己的问题,如果它有效 - 这样这个问题就在这里,以防其他人反对这个问题。否则我会屏住呼吸等待你的帮助:)
编辑:显然,如果其他人发布了一个可行的解决方案,如果更好,我会接受他们的回答而不是我的。
编辑(2):所以我的想法没有成功。事实证明,某些东西(可能是 range.insertNode)会神奇地为您平衡标签。我似乎没有足够的来自选择、范围或碎片的信息来始终知道选择是否在给定标签内。有什么想法吗?
最佳答案
编辑:这不是一个好的解决方案。它完全分解为比单行简单文本更复杂的任何东西。我已经解决了,很快就会发布更好的解决方案。
想通了。
我构建了一个数组,其中包含所见即所得区域中的每个文本节点及其父标签列表。
然后我将所选内容包装在一个自定义元素中,以便以后轻松删除,并且不与任何现有的 HTML 元素冲突,建议使用 x- 前缀。
然后我从该列表中重建所见即所得元素的内容,从选择的所有节点中删除单击按钮的标签(如果它们都已经有了),这是大多数所见即所得编辑器处理它的方式。
<button data-action="B"><b>b</b></button>
<button data-action="I"><i>i</i></button>
<button data-action="U"><u>u</u></button>
<p contenteditable>The quick brown fox jumps over the lazy dog.</p>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script>
$( function(){
var selectionWrapper = 'X-SELECTION';
function getTextData( element ) {
function getTextNodesIn( root ) {
var textNodes = [];
var parents = [];
function getTextNodes( node ) {
if( node.nodeType === 3 ){
var text = node.textContent;
textNodes.push({
text: text,
parents: parents.slice( 0 )
});
} else {
if( node !== root ){
parents.push( node.tagName );
}
for( var i = 0, len = node.childNodes.length; i < len; ++i ){
getTextNodes( node.childNodes[ i ] );
}
parents.pop();
}
}
getTextNodes( element );
return textNodes;
}
return getTextNodesIn( element );
}
function handleSelection( container, action ) {
var textData = getTextData( container );
container.innerHTML = '';
//if every textNode in the selection has action as a parent, we want
//to remove it from all of them.
var selection = _( textData ).filter( function( data ){
return _( data.parents ).contains( selectionWrapper );
});
var remove = _( selection ).every( function( data ) {
return _( data.parents ).contains( action ) || data.text.trim() === '';
});
_( selection ).each( function( data ){
if( remove ) {
data.parents = _( data.parents ).without( action );
} else {
data.parents.push( action );
}
});
//rebuild each text node
_( textData ).each( function( data ){
//no need to add empty text nodes
if( data.text === '' ) {
return;
}
//remove duplicates of the same parent tag and remove the selection wrapper
var parents = _( data.parents ).chain().uniq().without( selectionWrapper ).value();
var target = container;
_( parents ).each( function( parent ){
var node = document.createElement( parent );
target.appendChild( node );
target = node;
});
var text = document.createTextNode( data.text );
target.appendChild( text );
});
}
$( 'button' ).on( 'click', function(){
var action = $( this ).attr( 'data-action' );
var selection = window.getSelection();
for( var i = 0; i < selection.rangeCount; i++ ){
var range = selection.getRangeAt( i );
var node = document.createElement( selectionWrapper );
node.appendChild( range.extractContents() );
range.insertNode( node );
handleSelection( $( 'p' )[ 0 ], action );
}
return false;
});
});
</script>
关于javascript - 最小的 JavaScript 所见即所得 - 如果已存在,则从所选文本中删除标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15965720/