Forum Moderators: open
So far so good. The thing is that somehow all this creates a lot of undesired whitespace below the texarea, and I can't figure out what's wrong. Your input would be much appreciated.
I've tried to explicitly set the height of the parent element, but that didn't help. I've also messed around with floating the textarea and the ul, to no avail.
If you want to see the script in action, just save it somewhere on your server and link to it (<script src="..."></script> ) from this boards Custom Code Insert Top¦Bottom (in Control Panel > System Preferences). Then hit 'post new topic' or 'post reply'. I find it to be quite handy ;)
Here is the code:
var ta;
window.onload = function () {
// identify the textarea. there is only one. quit if none is found.
ta = document.getElementsByTagName('textarea')[0];
if (!ta)
return;
// attach event handlers to the textarea
addOrAttach(ta, 'select', storeCaretPos);
addOrAttach(ta, 'click', storeCaretPos);
addOrAttach(ta, 'change', storeCaretPos);
addOrAttach(ta, 'keyup', storeCaretPos);
addOrAttach(ta, 'dblclick', storeCaretPos);
addOrAttach(ta, 'mouseup', storeCaretPos);
// create list with edit controls
var ul = document.createElement('ul');
ul.appendChild(createLI("addTags('b')", 'bold'));
ul.appendChild(createLI("addTags('i')", 'italic'));
ul.appendChild(createLI("addTags('small')", 'small'));
ul.appendChild(createLI("addTags('quote')", 'quote'));
ul.appendChild(createLI("addTags('code')", 'code'));
ul.appendChild(createLI("addTags('pre')", 'pre'));
ul.appendChild(createLI("addTags('fixed')", 'fixed'));
ul.appendChild(createLI("addTags('url')", 'link'));
ul.appendChild(createLI("addTags('ul')", 'u-list'));
ul.appendChild(createLI("addTags('ol')", 'o-list'));
ta.parentNode.appendChild(ul);
// position the edit controls to the right of the textarea
ul.style.position = 'relative';
ul.style.top = '-' + ta.offsetHeight + 'px';
ul.style.left = ta.offsetWidth + 10 + 'px';
ul.style.width = '100px';
ul.style.border = '1px solid #fff';
ul.style.margin = 0;
ul.style.fontFamily = 'Verdana, sans-serif';
ul.style.fontSize = '12px';
ul.style.listStyleType = 'none';
}
function createLI(clickhandler, txt) {
var li = document.createElement('li');
li.style.margin = '0 0 0.3em 1em';
var a = document.createElement('a');
a.href = 'javascript:' + clickhandler;
a.appendChild(document.createTextNode(txt));
li.appendChild(a);
return li;
}
// Remember the current position.
function storeCaretPos() {
// only for supporting browsers
if (typeof(ta.createTextRange)!= 'undefined')
ta.caretPos = document.selection.createRange().duplicate();
}
function addOrAttach(el, ev, hdlr) {
if (el.attachEvent)
el.attachEvent('on' + ev, hdlr);
// geckos don't really use the eventhandler, but we'll create it anyway.
else if (el.addEventListener)
el.addEventListener(ev, hdlr, false);
}
function addTags(tag) {
switch (tag) {
case 'url' : var url = prompt("Please enter the URL:", "http://");
if (url)
surroundText('[url=' + url + ']', '[/url]', ta);
break;
case 'ul' :
case 'ol' : replaceText('\n['+tag+']\n[li.]\n[li.]\n[\/'+tag+']\n', ta); break;
// note: replace li. by li
case 'code' :
case 'pre' : surroundText('\n['+tag+']', '[/'+tag+']\n', ta); break;
default : surroundText('['+tag+']', '[/'+tag+']', ta); break;
}
}
// edit methods: replaceText, surroundText
function replaceText(txt, ta) {
// Attempt to create a text range (IE).
if (typeof(ta.caretPos)!= 'undefined' && ta.createTextRange) {
var caretPos = ta.caretPos;
caretPos.text = / $/.test(caretPos.text)? txt + ' ' : txt;
caretPos.select();
}
// geckos & Opera
else if (typeof(ta.selectionStart)!= 'undefined') {
var begin = ta.value.substr(0, ta.selectionStart);
var end = ta.value.substr(ta.selectionEnd);
ta.value = begin + txt + end;
if (ta.setSelectionRange) {
ta.focus();
ta.setSelectionRange(begin.length + txt.length, begin.length + txt.length);
}
}
// else put them on the end.
else {
ta.value += txt;
ta.focus(ta.value.length - 1);
}
}
function surroundText(text1, text2, ta) {
// Can a text range be created (IE)?
if (typeof(ta.caretPos)!= 'undefined' && ta.createTextRange) {
var caretPos = ta.caretPos;
// remove the whitespace IE adds on dblclick
caretPos.text = / $/.test(caretPos.text)?
text1 + caretPos.text.trim() + text2 + ' '
: text1 + caretPos.text + text2;
caretPos.select();
}
// geckos & Opera
else if (typeof(ta.selectionStart)!= 'undefined') {
var newCursorPos = ta.selectionEnd + text1.length + text2.length;
var chars = ta.value.split('');
chars.splice(ta.selectionStart, 0, text1);
chars.splice(ta.selectionEnd+1, 0, text2);
ta.value = chars.join('');
if (ta.setSelectionRange) {
ta.focus();
ta.setSelectionRange(newCursorPos, newCursorPos);
}
}
// else put them on the end.
else {
ta.value += text1 + text2;
ta.focus(ta.value.length - 1);
}
}
String.prototype.trim = function() {
return this.replace(/(^\s+¦\s+$)/, '');
// note: don't forget to fix the broken pipe above
}
I tried setting the height of the container and applying
overflow: hidden, but that didn't work. I already had been messing with floats and
display: inline: no luck there either. The easiest way turned out to be to give the list an absolute position and set the position of the container to relative. That makes it possible to set top and left relative to the containing td and not to the viewport (which would require some serious script to get the right values, cross-browser).
So, for the record, here are the first lines with styling rules for the list:
// position controls to the right of the textarea, by nesting absolute within relative
ta.parentNode.style.position = 'relative';
ta.parentNode.style.display = 'block';
ul.style.position = 'absolute';
ul.style.top = 0;