[JS] Absolute positie cursor textarea

Pagina: 1
Acties:
  • 139 views sinds 30-01-2008
  • Reageer

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Ik wil graag de x en y waarde van de cursor in een textarea hebben. Er zijn heel veel scripts te vinden op GoT en met Google om tekst in te voeren na de cursor, maar dat wil ik dus niet. Ik wil een suggestiebox toevoegen direct onder het punt waar je aan het typen bent. De suggestiebox is gewoon een absoluut gepositioneerd divje waarvan ik de left en top waardes dan verander. Daarvoor moet ik dus weten waar de cursor zich bevindt.

Een mogelijke oplossing was om als font courier te gebruiken (met vaste breedtes per letter dus) en voor elke getypte letter een aantal pixels toe te voegen aan de left. Het probleem is alleen dat je dan ook moet corrigeren voor backspace, delete en voor geselecteerde tekst die verwijderd wordt. Een echt makkelijke oplossing is dat dus niet. Daarnaast moet je nog newlines herkennen voor de top waarde (hoewel dat wel op te lossen is omdat je weet hoeveel letters er op een regel passen).

Iemand hier nog suggesties voor een makkelijkere oplossing?

edit: makkelijker is natuurlijk om [textarea].value.length*10 te gebruiken (als het font 10px breed is) 8)7. Goed, tis zondag zullen we maar zeggen. Nettere oplossingen zijn nog steeds welkom overigens.

[ Voor 9% gewijzigd door Wekko op 15-04-2007 16:25 ]


  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Een suggestiebox krijg je toch altijd op de plaats waar je aan het typen bent?

Als ik mijn cursor in een textarea heb staan, maar mijn muispijl kan evengoed op het ander scherm staan.

Going for adventure, lots of sun and a convertible! | GMT-8


  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Ik heb het over een custom-made suggestiebox die informatie uit een database haalt mbv Ajax.

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Ik zou het irritant vinden als zo'n autosuggest-box iedere keer verspringt. Ik zou eerder de autosuggest aan je textarea vastplakken. Of ben je een IDE aan het maken? Je kan trouwens niet vaule.length gebruiken, mensen kunnen hun cursor ook midden in de text zetten.

Verder kan je misschien ook wat met IE's document.selection.createRange en FF's window.getSelection().getRange(0).

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 19-10 08:18
even voor alle goede orde:

een cursor is me muispijltje,
een carrot is je knipperende rechtopstaande streepje op de plaats waar je typt.

de TS bedoelt in dit geval de carrot, niet de cursor.


@TS:
volgens mij is er geen methode in de web wereld (behalve misschien bij flash) die de mogelijkheid heeft om te kijken hoe 'lang' tekst is.

Dus de omrekening naar x,y coordinaten zal vrij lastig worden.
een monospace font is denk ik de enige mogelijkheid om dit netjes te doen.

een alternatief zou zijn dat je voor JOUW lettertype, en JOUW lettergrote PER letter in JS gaat opgeven hoe breed hij is. om vervolgens de totale breedte te berekenen. Echter, dit is erg ontoegankelijk, omdat wanneer de client zijn browser zo heeft ingesteld dat hij grotere tekst ziet dit al compleet niet meer werkt.

Dit valt dan weer te verhelpen door bij het loaden van de pagina dmv een calibratie te kijken hoe groot de client zijn font heeft ingesteld (lees: neem div, vul die met een aantal W's en kijk wanneer hij oprekt, deel die breedte door het aantal w's en je hebt een vaste maat) maar dit is enorm lastig en verschrikkelijk onnauwkeurig

edit2:
misschien kan het ook wel:
http://www.webmasterworld.com/forum91/5630.htm
(zelf geen tijd om te testen)

[ Voor 74% gewijzigd door BasieP op 15-04-2007 18:48 ]

This message was sent on 100% recyclable electrons.


  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
BasieP schreef op zondag 15 april 2007 @ 18:36:
even voor alle goede orde:

een cursor is me muispijltje,
een carrot is je knipperende rechtopstaande streepje op de plaats waar je typt.
Even voor de goede orde: een carrot is zo'n oranje ding dat in de grond groeit, je bedoelt een caret ;).

Een ding wat je zou kunnen doen:

Met de methodes van mij hierboven kan je de selectie (en dus ook cursorpositie) opvragen. Stel dat je ook weet waar alle newlines zitten, dan zou een mogelijk algoritme zo zijn:

code:
1
2
3
var line = getCurrentLine();
var caretPosition = getCurrentCaretPosition();
var textBeforeCaret = myText.getLines(line).substr(0, caretPosition);


De textBeforeCaret kan je vervolgens in een span stoppen met precies hetzelfde font, en daarvan kan je weer de breedte meten (element.offsetWidth). Die span moet je ergens in een hidden div stoppen ofzo.

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Hmm dat is op zich wel een hele slimme oplossing voor de x waarde, maar nog niet voor de y waarde. Het probleem is namelijk dat je browser automatisch aan word-wrap doet in een textarea. Je kunt dit dus niet detecteren aan een \n, dat kan alleen met enters. De hoogte meten in een hidden span is dan wel weer een optie aangezien de hoogte van letters wel hetzelfde is (iig de ruimte die er voor gereserveerd is)...ik ga het eens proberen. Thanx voor de suggesties iig allen.

Verwijderd

Je kan wordwrap uitzetten in een textarea, of is dat niet zo handig ?

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
ja dat heb ik nu ook gedaan. Dit moet alleen met een niet-standaard attribute (wrap="off" bestaat officieel niet). Via CSS werkt het niet in IE7 en FF.

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 19-10 08:18
klinkt misschien beetje raar, maar waarom gebruik je een textarea? dit is een van de lastigste elementen in html, aangezien hij bijna alles standaard verkeerd doet (denk aan font, wrapping, markup etc.)
de meeste WYSIWYG editors gebruiken JS om keys mee af te vangen, en voegen die tekens dan toe aan een divje oid.
Deze zijn een stuk makkelijker op te maken.

gewoon om het iig gezegt te hebben. ;)

This message was sent on 100% recyclable electrons.


  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Ik heb er wel aan zitten denken ja. Ik ben alleen bang dat je, zeker op wat oudere pc's, een delay krijgt met typen. Maar wellicht is dat sowieso al het geval door die suggestiebox...dat is sowieso iets om te testen. Misschien lost dat het positioneerprobleem ook wel deels op door er een span omheen te gooien ;).

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
hmm ben nu bezig met ombouwen maar loop al tegen twee problemen aan. De eerste is de backspace knop die ook als back knop fungeert in meeste browsers. Dit is mijn code om de letters af te vangen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
document.onkeydown = myKeyDown;

function myKeyDown(e){
   var code;
    if (!e) var e                                = window.event;
    if (e.keyCode) code                          = e.keyCode;
    else if (e.which) code                       = e.which;
    keyCodesPressed[keyCodesPressed.length]      = code;
    keysPressed[keysPressed.length]              = String.fromCharCode(code);

    myObj.myFunction(); 
   return false;    
}


Wanneer ik de functieaanroep (hier even myObj.myFunction() genoemd) weghaal werkt het perfect. Met bovenstaande code dus niet. Maar ik moet die functie wel aanroepen.

Mijn tweede probleem is dat ik alleen maar hoofdletters terugkrijg. Als ik een alert(code); toevoeg krijg ik ook dezelfde code terug bij een hoofd- en kleine letter (dus F en f resulteert in code 70).

Iemand enig idee?

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Wekko schreef op woensdag 18 april 2007 @ 10:21:
hmm ben nu bezig met ombouwen maar loop al tegen twee problemen aan. De eerste is de backspace knop die ook als back knop fungeert in meeste browsers. Dit is mijn code om de letters af te vangen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
document.onkeydown = myKeyDown;

function myKeyDown(e){
   var code;
    if (!e) var e                                = window.event;
    if (e.keyCode) code                          = e.keyCode;
    else if (e.which) code                       = e.which;
    keyCodesPressed[keyCodesPressed.length]      = code;
    keysPressed[keysPressed.length]              = String.fromCharCode(code);

    myObj.myFunction(); 
   return false;    
}


Wanneer ik de functieaanroep (hier even myObj.myFunction() genoemd) weghaal werkt het perfect. Met bovenstaande code dus niet. Maar ik moet die functie wel aanroepen.
Ik zie niet hoe je aan die myObj komt? Ik zou verwachten dat 'ie inderdaad null is.
Mijn tweede probleem is dat ik alleen maar hoofdletters terugkrijg. Als ik een alert(code); toevoeg krijg ik ook dezelfde code terug bij een hoofd- en kleine letter (dus F en f resulteert in code 70).
Je kan aan de browser vragen of de shift-key is ingedrukt.

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Ik zie niet hoe je aan die myObj komt? Ik zou verwachten dat 'ie inderdaad null is.
Dat is een global object. Dit is ook niet mijn volledige code uiteraard, heb alleen het belangrijke er tussen uitgehaald. Maar ik heb zojuist een oplossing gevonden voor het backspace probleem:
http://webdev.kevinlloyd....ents-using-javascript.htm
Je kan aan de browser vragen of de shift-key is ingedrukt.
ik heb het opgelost door alles te vertalen naar kleine letters. Het probleem is namelijk dat wanneer je shift inhoudt dat ie een hele stroom van shifts doorgeeft. Op zich is er wel omheen te bouwen, maar ik denk dat dat nu niet zo belangrijk is.

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 07:22

orf

Niet perfect, maar wel redelijk werkzaam:

online testje hier

HTML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Untitled</title>
<style type="text/css">
body, html, textarea{
    font: 11px Arial, Helvetica, sans-serif;
}
textarea{
    width: 300px;
    height: 200px;
}
.listener_container{
    width: 300px;
    height: 200px;
    overflow: auto;
    border: 1px solid #ccc; 
    white-space: inherit; 
    visibility: hidden;
}

#tooltip{
    background: #FFFF80;
    border: 1px solid #ccc;
    font-size: 10px;
    width: 40px;
    height: 13px;
    position: absolute;
    text-align: center;
    top: 100px;
    left: 100px;
}
</style>
<script type="text/javascript">
onload = function(){
    var text_input = document.getElementById('text_input');
    text_input.tooltip =  document.getElementById('tooltip');
    text_input.onkeyup = function(){
        if (!this.listener){
            this.listener = document.getElementById('listener');
        }
        this.listener.innerHTML = nl2br(this.value.substr(0, getCaretPosition(this))) + '&nbsp;';
        positionTooltip(this.tooltip, this.listener, this);
    }
}
function nl2br(text){    
    text = escape(text);   
    var nwline; 
    if(text.indexOf('%0D%0A') > -1){        
        nwline = /%0D%0A/g;    
    }else if(text.indexOf('%0A') > -1){        
        nwline = /%0A/g;    
    }else if(text.indexOf('%0D') > -1){        
        nwline = /%0D/g;    
    }    
    return unescape(text.replace(nwline,'<br>')).replace(/\s\s/g, ' &nbsp;');
}

function positionTooltip(tooltip, listener, textarea){
    tooltip.style.top =  getOffsetTop(textarea) + min(listener.offsetHeight, listener.parentNode.offsetHeight) + 'px';
    tooltip.style.left =  getOffsetLeft(textarea) + getWidth(listener) + 'px';  
}

function min(val1, val2){
    if (val1 > val2){
        return val2;
    } else {
        return val1;
    }
}
function getWidth(obj){
    if (!this.width_listener){
        this.width_listener = document.getElementById('width_listener');
    }
    var a =  obj.innerHTML.split('<br>');
    this.width_listener.innerHTML = a[a.length-1];
    return min(obj.offsetWidth, this.width_listener.offsetWidth);
}

function getOffsetTop(el){
    var offsetTop = 0;
    do {
      offsetTop += el.offsetTop
    } while ((el = el.offsetParent));
    return offsetTop;
}
function getOffsetLeft(el){
    var offsetLeft = 0;
    do {
      offsetLeft += el.offsetLeft
    } while ((el = el.offsetParent));
    return offsetLeft;
}

function getCaretPosition (ctrl) {    
    var CaretPos = 0;      
    if (document.selection) {        
        ctrl.focus ();        
        var Sel = document.selection.createRange ();        
        Sel.moveStart ('character', -ctrl.value.length);        
        CaretPos = Sel.text.length;    
    }       
    else if (ctrl.selectionStart || ctrl.selectionStart == 0)        
    CaretPos = ctrl.selectionStart;    
    return (CaretPos);
}


</script>
</head>
<body>
<form method="get" action="#">
    <p><textarea rows="6" cols="6" id="text_input"></textarea>
</form>
<div class="listener_container"><span id="listener"></span></div>    
<div class="listener_container"><span id="width_listener"></span></div>
<div id="tooltip">Tooltip</div>
</body>
</html>


edit: werkt in Firefox beter dan in IE

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
orf schreef op donderdag 19 april 2007 @ 21:50:
Niet perfect, maar wel redelijk werkzaam:

online testje hier
Supercool! Je moet 'm ook ng even laten luisteren naar mouseclicks, dan is 'ie helemaal perfect.

  • Wekko
  • Registratie: Januari 2000
  • Laatst online: 28-11 17:00
Dat ziet er wel uit als een hele mooie oplossing orf, ik krijg alleen niet het gewenste resultaat in IE7 en FireFox wanneer de volgende regel aanbreekt..een enter werkt wel, maar een auto-wordwrap niet.

Ik heb het zelf nu opgelost door een span in de div (dus geen textarea meer) te zetten aan het eind van de tekst en daarvan de positie te bepalen. Maar dat lost dus niet (zoals bij jou) het probleem op van een muisklik die de cursorverplaatst (wat bij jouw oplossing dus wel werkt).

Ik zal eens kijken of er wat te mixen valt aan onze code ;). Thanx iig

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 07:22

orf

Het was een eerste opzetje om eens wat te experimenteren. Als ik dit ergens in zou moeten bouwen, dan zou ik de wordwrap in een hidden div zelf opbouwen: Kijk hoe breed een string is en voeg een <br> op het moment dat de string te breed wordt (positie van de laatste spatie in de string).

Dan kun je voor de X positie altijd de laatste regel pakken van de substring.

Succes ermee :)
Pagina: 1