I'm trying to adjust the sizes and locations of the tiles in this DCL dialog box. Basically, I want to make the edit-boxes in the Client box, Job box, and Miscellaneous box to be the same width. However, they each start at different widths due to the different sizes of the text tile's labels. Just setting the width to a fixed value for both edit-box and text tiles doesn't seem to be fixing the issue for me. How do I need to change this code to make the tiles within this dialog box uniform?
As a bonus, I want to also adjust the tiles in the Revision box. I'm assuming this will be using the same solution. I want to have the widths of the edit-boxes of different widths with the labels centered above them.
I have found that DCL files are a slow and painful process for me. Any help with building this dialog box would be helpful. Thank you for your time.
AutoLisp Code:
(defun C:Test01 (/ sPathAndName sDCLModuleName dclFile bContinue)
;; Initializing
(setq sPathAndName "[File's location and name]")
(setq sDCLModuleName "TitleRevUpdate")
(setq bContinue T)
;; File Exists
(if (not (findfile sPathAndName))(progn
(princ "\nError: The DCL file was not found.\n")
(setq bContinue nil)
));if<-progn
;; DCL File
(if bContinue (progn
(setq dclFile (load_dialog sPathAndName))
(if (>= 0 dclFile)(progn
(princ "\nError: DCL file cannot be loaded.\n")
(setq bContinue nil)
));if<-progn
));if<-progn
;; Creating a new module
(if bContinue
(setq bContinue (new_dialog sDCLModuleName dclFile "" '(-1 -1)))
);if
;; Build and run DCL module
(if bContinue (progn
;; User Form
(princ "\nstart_dialog : ")(princ (start_dialog))(terpri)
(unload_dialog dclFile)
));if<-progn
);C:Test01
DCL Code:
TitleRevUpdate : dialog {
key = "Title" ;
label = "Update Title Block and Revision" ;
// Title
: boxed_column {
key = "Column_TitleBoxes" ;
label = "Title" ;
// Client
: boxed_column {
key = "Client_Box" ;
label = "Client" ;
: row { // Row 01 - Name
key = "Row_Client_Name" ;
width = 15 ;
: text {
key = "txt_Client_Name" ;
alignment = right ;
label = "Client's Name" ;
width = 10 ;
}// text
: edit_box {
key = "edbx_Client_Name" ;
alignment = left ;
width = 10 ;
}// edit_box
} //row
: row { // Row 02 - Location
key = "Row_Client_Loc" ;
: text {
key = "txt_Client_Loc" ;
alignment = right ;
label = "Client's Location" ;
width = 10 ;
}// text
: edit_box {
key = "edbx_Client_Loc" ;
alignment = left ;
width = 10 ;
}// edit_box
} //row
} //boxed_column
: spacer {
}// spacer
// Job
: boxed_column {
key = "Job_Box" ;
label = "Job" ;
: row { // Row 03 - Name
key = "Row_Job_Name" ;
: text {
key = "txt_Job_Name" ;
label = "Job's Name" ;
}// text
: edit_box {
key = "edbx_Job_Name" ;
}// edit_box
} //row
: row { // Row 04 - Number
key = "Row_Job_Number" ;
: text {
key = "txt_Job_Number" ;
label = "Job's Number" ;
}// text
: edit_box {
key = "edbx_Job_Number" ;
}// edit_box
} //row
} //boxed_column
: spacer {
}// spacer
// Miscellaneous
: boxed_column {
key = "Miscellaneous_Box" ;
label = "Miscellaneous" ;
: row { // Row 05 - Creator's Initials
key = "Row_Creator_Name" ;
: text {
key = "txt_Creator_Name" ;
label = "Creator's Name" ;
}// text
: edit_box {
key = "edbx_Creator_Name" ;
}// edit_box
} //row
: row { // Row 06 - Date of Creation
key = "Row_Date" ;
: text {
key = "txt_TitleDate" ;
label = "Date" ;
}// text
: edit_box {
key = "edbx_TitleDate" ;
}// edit_box
} //row
: row { // Row 07 - Issued For
key = "Row_Issued_For" ;
: text {
key = "txt_Issued_For" ;
label = "Issued For" ;
}// text
: edit_box {
key = "edbx_Issued_For" ;
}// edit_box
} //row
} //boxed_column
} //boxed_column
: spacer {
}// spacer
// Revision
: boxed_column {
key = "Column_Revision" ;
label = "Revision" ;
: row { // Row 08 - Quick Choices
key = "Row_Buttons" ;
: button {
key = "btn_IFC" ;
label = "Issued for Construction" ;
}// button
: button {
key = "tbn_AB" ;
label = "As Built" ;
}// button
: radio_column {
key = "RadioCol_WriteMethod" ;
: radio_button {
key = "rbtn_Owt" ;
label = "Clear && Overwrite" ;
}// radio_button
: radio_button {
key = "rbtn_Apnd" ;
label = "Append / New Line" ;
}// radio_button
} //radio_column
} //row
: spacer {
}// spacer
: row { // Row 09 - Rev Labels
key = "Row_Labels" ;
: text {
key = "txt_Rev" ;
label = "Rev" ;
}// text
: text {
key = "txt_Initials" ;
label = "Initials" ;
}// text
: text {
key = "txt_Description" ;
label = "Description" ;
}// text
: text {
key = "txt_RevDate" ;
label = "Date" ;
}// text
} //row
: row { // Row 10 - Rev Edit Boxes
key = "Row_Rev" ;
: edit_box {
key = "edbx_Rev" ;
}// edit_box
: edit_box {
key = "edbx_Initials" ;
}// edit_box
: edit_box {
key = "edbx_Date" ;
}// edit_box
: edit_box {
key = "edbx_RevDate" ;
}// edit_box
} //row
} //boxed_column
: spacer {
}// spacer
// Return Commands
: row { // Row 11 - Buttons
key = "Row_Return" ;
: button {
key = "btn_DWGs" ;
action = "(done_dialog 2)" ;
label = "Show Drawings" ;
}// button
: button {
key = "btn_Confirm" ;
action = "(done_dialog 1)" ;
is_enabled = true ;
label = "Confirm" ;
}// button
: button {
key = "btn_Cancel" ;
action = "(done_dialog 0)" ;
is_default = true ;
label = "Cancel" ;
}// button
} //row
: spacer {
}// spacer
} // TitleRevUpdate
The key to edit box alignment in standard DCL is to set the width attribute to be the same for all edit boxes (and large enough to accommodate the text and editing area of the largest edit box in the group), set the fixed_width attribute to true for all edit boxes, and then control the size of the edit box itself using the edit_width attribute.
You don't need separate text tiles when working with edit boxes, instead, you can use the label attribute of the edit_box tile.
For example, the upper section of your DCL could be revised to the following:
TitleRevUpdate : dialog
{
key = "Title" ;
label = "Update Title Block and Revision" ;
spacer;
// Title
: boxed_column
{
label = "Title" ;
// Client
: boxed_column
{
label = "Client" ;
: edit_box
{
label = "Client's Name:";
key = "edbx_Client_Name";
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
: edit_box
{
label = "Client's Location:" ;
key = "edbx_Client_Loc" ;
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
spacer;
}
spacer;
// Job
: boxed_column
{
label = "Job";
: edit_box
{
label = "Job's Name:";
key = "edbx_Job_Name";
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
: edit_box
{
label = "Job's Number:" ;
key = "edbx_Job_Number" ;
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
spacer;
}
spacer;
// Miscellaneous
: boxed_column
{
label = "Miscellaneous" ;
: edit_box
{
key = "edbx_Creator_Name" ;
label = "Creator's Name:" ;
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
: edit_box
{
key = "edbx_TitleDate" ;
label = "Date:" ;
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
: edit_box
{
key = "edbx_Issued_For" ;
label = "Issued For:" ;
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
spacer;
}
spacer;
}
spacer;
// Return Commands
: row
{
key = "Row_Return" ;
fixed_width = true;
alignment = centered;
: button
{
key = "btn_DWGs" ;
action = "(done_dialog 2)" ;
label = "Drawings" ;
fixed_width = true;
width = 16;
}
: button
{
key = "btn_Confirm" ;
action = "(done_dialog 1)" ;
is_enabled = true ;
label = "Confirm" ;
fixed_width = true;
width = 16;
}
: button
{
key = "btn_Cancel" ;
action = "(done_dialog 0)" ;
is_default = true ;
label = "Cancel" ;
fixed_width = true;
width = 16;
}
}
spacer;
}
To yield the following GUI:
And given that all of the tiles are sharing the same attribute values, you can define a named tile to condense the code much further, e.g.:
myeditbox : edit_box
{
alignment = left;
width = 52;
fixed_width = true;
edit_width = 30;
}
mybutton : button
{
fixed_width = true;
width = 16;
}
TitleRevUpdate : dialog
{
key = "Title" ;
label = "Update Title Block and Revision" ;
spacer;
: boxed_column
{
label = "Title" ;
: boxed_column
{
label = "Client" ;
: myeditbox { label = "Client's Name:"; key = "edbx_Client_Name"; }
: myeditbox { label = "Client's Location:"; key = "edbx_Client_Loc" ; }
spacer;
}
spacer;
: boxed_column
{
label = "Job";
: myeditbox { label = "Job's Name:"; key = "edbx_Job_Name"; }
: myeditbox { label = "Job's Number:"; key = "edbx_Job_Number"; }
spacer;
}
spacer;
: boxed_column
{
label = "Miscellaneous" ;
: myeditbox { label = "Creator's Name:"; key = "edbx_Creator_Name"; }
: myeditbox { label = "Date:"; key = "edbx_TitleDate"; }
: myeditbox { label = "Issued For:"; key = "edbx_Issued_For"; }
spacer;
}
spacer;
}
spacer;
: row
{
key = "Row_Return" ;
fixed_width = true;
alignment = centered;
: mybutton { key = "btn_DWGs"; action = "(done_dialog 2)"; label = "Drawings"; }
: mybutton { key = "btn_Confirm"; action = "(done_dialog 1)"; label = "Confirm"; is_enabled = true; }
: mybutton { key = "btn_Cancel" ; action = "(done_dialog 0)"; label = "Cancel"; is_default = true; }
}
spacer;
}
Except
width = 10 ;
You need also
fixed_width = true;
on each edit_box
I am using jsPDF in my site to generate PDFs. But now I have multiple DIVs to print in a single PDF. which may take 2 to 3 pages.
For example:
<div id="part1">
content
</div>
<div id="part2">
content
</div>
<div id="part2">
content
</div>
my JS code
This works but not as I expected, It add a part of the content(which cannot be included in more than one page).
It removes html tags like br, h1 etc.
function formtoPDF() {
jsPDF.API.mymethod = function() {
// 'this' will be ref to internal API object. see jsPDF source
// , so you can refer to built-in methods like so:
// this.line(....)
// this.text(....)
};
var doc = new jsPDF();
doc.mymethod();
var pdfPart1 = jQuery('#genPDFpart1');
var pdfPart2 = jQuery(".ltinerary");
var pdfPart3 = jQuery("#domElementHTML");
var specialElementHandlers = {
'#loadVar': function(element, renderer) {
return true;
}
};
doc.fromHTML(pdfPart1.html() + pdfPart3.html() + pdfPart3.html(), 15, 15, {
'width': 170,
'elementHandlers': specialElementHandlers
});
doc.output('save', 'Download.pdf');
}
What's the solution for this?
I have the same working issue. Searching in MrRio github I found this: https://github.com/MrRio/jsPDF/issues/101
Basically, you have to check the actual page size always before adding new content
doc = new jsPdf();
...
pageHeight= doc.internal.pageSize.height;
// Before adding new content
y = 500 // Height position of new content
if (y >= pageHeight)
{
doc.addPage();
y = 0 // Restart height position
}
doc.text(x, y, "value");
here's an example using html2canvas & jspdf, although how you generate the canvas doesn't matter--we're just going to use the height of that as the breakpoint on a for loop, in which a new page is created and content added to it.
after the for loop, the pdf is saved.
function makePDF() {
var quotes = document.getElementById('container-fluid');
html2canvas(quotes).then((canvas) => {
//! MAKE YOUR PDF
var pdf = new jsPDF('p', 'pt', 'letter');
for (var i = 0; i <= quotes.clientHeight/980; i++) {
//! This is all just html2canvas stuff
var srcImg = canvas;
var sX = 0;
var sY = 980*i; // start 980 pixels down for every new page
var sWidth = 900;
var sHeight = 980;
var dX = 0;
var dY = 0;
var dWidth = 900;
var dHeight = 980;
window.onePageCanvas = document.createElement("canvas");
onePageCanvas.setAttribute('width', 900);
onePageCanvas.setAttribute('height', 980);
var ctx = onePageCanvas.getContext('2d');
// details on this usage of this function:
// https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);
// document.body.appendChild(canvas);
var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);
var width = onePageCanvas.width;
var height = onePageCanvas.clientHeight;
//! If we're on anything other than the first page,
// add another page
if (i > 0) {
pdf.addPage(612, 791); //8.5" x 11" in pts (in*72)
}
//! now we declare that we're working on that page
pdf.setPage(i+1);
//! now we add content to that page!
pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62));
}
//! after the for loop is finished running, we save the pdf.
pdf.save('Test.pdf');
});
}
I found the solution on this page: https://github.com/MrRio/jsPDF/issues/434
From the user: wangzhixuan
I copy the solution here:
// suppose your picture is already in a canvas
var imgData = canvas.toDataURL('image/png');
/*
Here are the numbers (paper width and height) that I found to work.
It still creates a little overlap part between the pages, but good enough for me.
if you can find an official number from jsPDF, use them.
*/
var imgWidth = 210;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save( 'file.pdf');
var doc = new jsPDF('p', 'mm');
var imgData = canvas.toDataURL('image/png');
var pageHeight= doc.internal.pageSize.getHeight();
var pageWidth= doc.internal.pageSize.getWidth();
var imgheight = $('divName').height() * 25.4 / 96; //px to mm
var pagecount = Math.ceil(imgheight / pageHeight);
/* add initial page */
doc.addPage('l','mm','a4');
doc.addImage(imgData, 'PNG', 2, 0, pageWidth-4, 0);
/* add extra pages if the div size is larger than a a4 size */
if (pagecount > 0) {
var j = 1;
while (j != pagecount) {
doc.addPage('l','mm','a4');
doc.addImage(imgData, 'PNG', 2, -(j * pageHeight), pageWidth-4, 0);
j++;
}
}
You can use html2canvas plugin and jsPDF both. Process order:
html to png & png to pdf
Example code:
jQuery('#part1').html2canvas({
onrendered: function( canvas ) {
var img1 = canvas.toDataURL('image/png');
}
});
jQuery('#part2').html2canvas({
onrendered: function( canvas ) {
var img2 = canvas.toDataURL('image/png');
}
});
jQuery('#part3').html2canvas({
onrendered: function( canvas ) {
var img3 = canvas.toDataURL('image/png');
}
});
var doc = new jsPDF('p', 'mm');
doc.addImage( img1, 'PNG', 0, 0, 210, 297); // A4 sizes
doc.addImage( img2, 'PNG', 0, 90, 210, 297); // img1 and img2 on first page
doc.addPage();
doc.addImage( img3, 'PNG', 0, 0, 210, 297); // img3 on second page
doc.save("file.pdf");
$( document ).ready(function() {
$('#cmd').click(function() {
var options = {
pagesplit: true //include this in your code
};
var pdf = new jsPDF('p', 'pt', 'a4');
pdf.addHTML($("#pdfContent"), 15, 15, options, function() {
pdf.save('Menu.pdf');
});
});
});
This is my first post which support only a single page http://www.techumber.com/html-to-pdf-conversion-using-javascript/
Now, the second one will support the multiple pages.
http://www.techumber.com/how-to-convert-html-to-pdf-using-javascript-multipage/
Below is my code but the problem is that the document doesn't split to display the other part of the document in a new page.
Please improve this code.
<script type='text/javascript'>
$(document).on("click", "#btnExportToPDF", function () {
var table1 =
tableToJson($('#table1')[0]),
cellWidth =42,
rowCount = 0,
cellContents,
leftMargin = 2,
topMargin = 12,
topMarginTable =5,
headerRowHeight = 13,
rowHeight = 12,
l = {
orientation: 'p',
unit: 'mm',
format: 'a3',
compress: true,
fontSize: 11,
lineHeight: 1,
autoSize: false,
printHeaders: true
};
var doc = new jsPDF(l,'pt', 'letter');
doc.setProperties({
title: 'Test PDF Document',
subject: 'This is the subject',
author: 'author',
keywords: 'generated, javascript, web 2.0, ajax',
creator: 'author'
});
doc.cellInitialize();
$.each(table1, function (i, row)
{
rowCount++;
$.each(row, function (j, cellContent) {
if (rowCount == 1) {
doc.margins = 1;
doc.setFont("Times New Roman");
doc.setFontType("bold");
doc.setFontSize(11);
doc.cell(leftMargin, topMargin, cellWidth, headerRowHeight, cellContent, i)
}
else if (rowCount == 2) {
doc.margins = 1;
doc.setFont("Times ");
doc.setFontType("normal");
// or for normal font type use ------ doc.setFontType("normal");
doc.setFontSize(11);
doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i);
}
else {
doc.margins = 1;
doc.setFont("Times ");
doc.setFontType("normal ");
doc.setFontSize(11);
doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i);
// 1st=left margin 2nd parameter=top margin, 3rd=row cell width 4th=Row height
}
})
})
doc.save('sample Report.pdf');
});
function tableToJson(table) {
var data = [];
// first row needs to be headers
var headers = [];
for (var i=0; i<table.rows[0].cells.length; i++) {
headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi,'');
}
// go through cells
for (var i=1; i<table.rows.length; i++) {
var tableRow = table.rows[i];
var rowData = {};
for (var j=0; j<tableRow.cells.length; j++) {
rowData[ headers[j] ] = tableRow.cells[j].innerHTML;
}
data.push(rowData);
}
return data;
}
</script>
Automatically not split data to multi pages. You may split manually.
If your ( rowCount * rowHeight ) > 420mm ( A3 Height in mm ) add new page function. ( Sorry I can't edit your code without run )
After add new page leftMargin, topMargin = 0; ( start over )
I added sample code with yours. I hope it's right.
else {
doc.margins = 1;
doc.setFont("Times ");
doc.setFontType("normal ");
doc.setFontSize(11);
if ( rowCount * rowHeight > 420 ) {
doc.addPage();
rowCount = 3; // skip 1 and 2 above
} else {
// now rowcount = 3 ( top of new page for 3 )
// j is your x axis cell index ( j start from 0 on $.each function ) or you can add cellCount like rowCount and replace with
// rowcount is your y axis cell index
left = ( ( j ) * ( cellWidth + leftMargin );
top = ( ( rowcount - 3 ) * ( rowHeight + topMargin );
doc.cell( leftMargin, top, cellWidth, rowHeight, cellContent, i);
// 1st=left margin 2nd parameter=top margin, 3rd=row cell width 4th=Row height
}
}
You can convert html directly to pdf lossless. Youtube video for html => pdf example
html2canvas(element[0], {
onrendered: function (canvas) {
pages = Math.ceil(element[0].clientHeight / 1450);
for (i = 0; i <= pages; i += 1) {
if (i > 0) {
pdf.addPage();
}
srcImg = canvas;
sX = 0;
sY = 1450 * i;
sWidth = 1100;
sHeight = 1450;
dX = 0;
dY = 0;
dWidth = 1100;
dHeight = 1450;
window.onePageCanvas = document.createElement("canvas");
onePageCanvas.setAttribute('width', 1100);
onePageCanvas.setAttribute('height', 1450);
ctx = onePageCanvas.getContext('2d');
ctx.drawImage(srcImg, sX, sY, sWidth, sHeight, dX, dY, dWidth, dHeight);
canvasDataURL = onePageCanvas.toDataURL("image/png");
width = onePageCanvas.width;
height = onePageCanvas.clientHeight;
pdf.setPage(i + 1);
pdf.addImage(canvasDataURL, 'PNG', 35, 30, (width * 0.5), (height * 0.5));
}
pdf.save('testfilename.pdf');
}
});
var a = 0;
var d;
var increment;
for(n in array){
d = a++;
if(n % 6 === 0 && n != 0){
doc.addPage();
a = 1;
d = 0;
}
increment = d == 0 ? 10 : 50;
size = (d * increment) <= 0 ? 10 : d * increment;
doc.text(array[n], 10, size);
}
I am trying to implement Autocomplete in a text area (similar to http://www.pengoworks.com/workshop/jquery/autocomplete.htm).
What I am trying to do is when a user enters a specific set of characters (say insert:) they will get an AJAX filled div with possible selectable matches.
In a regular text box, this is of course simple, but in a text area I need to be able to popup the div in the correct location on the screen based on the cursor.
Can anyone provide any direction?
Thanks,
-M
You can get the caret using document.selection.createRange(), and then examining it to reveal all the information you need (such as position). See those examples for more details.
Implementing an autocomplete in a text area is not that easy. I implemented a jquery plugin that does that, and i had to create a clone of the texarea to guess where the cursor is positioned inside the textarea.
Its working, but its not perfect.
You can check it out here: http://www.amirharel.com/2011/03/07/implementing-autocomplete-jquery-plugin-for-textarea/
I hope it helps.
function getCursor(nBox){
var cursorPos = 0;
if (document.selection){
nBox.focus();
var tmpRange = document.selection.createRange();
tmpRange.moveStart('character',-nBox.value.length);
cursorPos = tmpRange.text.length;
}
else{
if (nBox.selectionStart || nBox.selectionStart == '0'){
cursorPos = nBox.selectionStart;
}
}
return cursorPos;
}
function detectLine(nBox,lines){
var cursorPos = getCursor(nBox);
var z = 0; //Sum of characters in lines
var lineNumber = 1;
for (var i=1; i<=lines.length; i++){
z = sumLines(i)+i; // +i because cursorPos is taking in account endcharacters of each line.
if (z >= cursorPos){
lineNumber = i;
break;
}
}
return lineNumber;
function sumLines(arrayLevel){
sumLine = 0;
for (var k=0; k<arrayLevel; k++){
sumLine += lines[k].length;
}
return sumLine;
}
}
function detectWord(lineString, area, currentLine, linijeKoda){
function sumWords(arrayLevel){
var sumLine = 0;
for (var k=0; k<arrayLevel; k++){
sumLine += words[k].length;
}
return sumLine;
}
var cursorPos = getCursor(area);
var sumOfPrevChars =0;
for (var i=1; i<currentLine; i++){
sumOfPrevChars += linijeKoda[i].length;
}
var cursorLinePos = cursorPos - sumOfPrevChars;
var words = lineString.split(" ");
var word;
var y = 0;
for(var i=1; i<=words.length; i++){
y = sumWords(i) + i;
if(y >= cursorLinePos){
word = i;
break;
}
}
return word;
}
var area = document.getElementById("area");
var linijeKoda = area.value.split("\n");
var currentLine = detectLine(area,linijeKoda);
var lineString = linijeKoda[currentLine-1];
var activeWord = detectWord(lineString, area, currentLine, linijeKoda);
var words = lineString.split(" ");
if(words.length > 1){
var possibleString = words[activeWord-1];
}
else{
var possibleString = words[0];
}
That would do it ... :)
an ugly solution:
for ie: use document.selection...
for ff: use a pre behind textarea, paste text before cursor into it, put a marker html element after it (cursorPos), and get the cursor position via that marker element
Notes: | code is ugly, sorry for that | pre and textarea font must be the same | opacity is utilized for visualization | there is no autocomplete, just a cursor following div here (as you type inside textarea) (modify it based on your need)
<html>
<style>
pre.studentCodeColor{
position:absolute;
margin:0;
padding:0;
border:1px solid blue;
z-index:2;
}
textarea.studentCode{
position:relative;
margin:0;
padding:0;
border:1px solid silver;
z-index:3;
overflow:visible;
opacity:0.5;
filter:alpha(opacity=50);
}
</style>
hello world<br/>
how are you<br/>
<pre class="studentCodeColor" id="preBehindMyTextarea">
</pre>
<textarea id="myTextarea" class="studentCode" cols="100" rows="30" onkeyup="document.selection?ieTaKeyUp():taKeyUp();">
</textarea>
<div
style="width:100px;height:60px;position:absolute;border:1px solid red;background-color:yellow"
id="autoCompleteSelector">
autocomplete contents
</div>
<script>
var myTextarea = document.getElementById('myTextarea');
var preBehindMyTextarea = document.getElementById('preBehindMyTextarea');
var autoCompleteSelector = document.getElementById('autoCompleteSelector');
function ieTaKeyUp(){
var r = document.selection.createRange();
autoCompleteSelector.style.top = r.offsetTop;
autoCompleteSelector.style.left = r.offsetLeft;
}
function taKeyUp(){
taSelectionStart = myTextarea.selectionStart;
preBehindMyTextarea.innerHTML = myTextarea.value.substr(0,taSelectionStart)+'<span id="cursorPos">';
cp = document.getElementById('cursorPos');
leftTop = findPos(cp);
autoCompleteSelector.style.top = leftTop[1];
autoCompleteSelector.style.left = leftTop[0];
}
function findPos(obj) {
var curleft = curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
}
return [curleft,curtop];
}
//myTextarea.selectionStart
</script>
</html>