[JS] DOM aanpassen lukt niet

Pagina: 1
Acties:

  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Ik probeer voor het eerst om object georienteerd te javascripten, en de DOM aan te passen ipv document.write's te gebruiken. Wat ik wil is het volgende:

Ik heb een pagina met daarop een textarea met als class "txtEditor". Een javascriptje scant de pagina en filtert alle textarea's met die class eruit. Vervolgens moet deze worden omgetoverd in een texteditor. In HTML moet het van dit
HTML:
1
<textarea name="bericht" class="textEditor">Testcontent</textarea>

naar ongeveer dit:
HTML:
1
2
3
4
5
6
7
<div id="bericht-txtContainer" class="bericht-txtContainer">
  <ul>
    <li><a href="#" class="txtButtonBold">Bold</a></li>
    <li><a href="#" class="txtButtonLink">Hyperlink</a></li>
  </ul>
  <textarea name="bericht-txtTextarea" class="txtEditor">Testcontent</textarea>
</div>

Met andere woorden: ik wil een toolbar boven de textarea zetten en de naam van de textarea veranderen ivm css classes. Ik heb nu (onder andere) de volgende code:
JavaScript:
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
var txtToolbarItems = ['bold', 'link']

window.onload = init;

function init() {
  var theTextareas = document.getElementsByTagName("textarea");
  for (var i = 0; i < theTextareas.length; i++) {
    var theTextarea = theTextareas[i];
    if (theTextarea.className.classExists("textEditor")) {
      if (theTextarea.id == "") {
        theTextarea.id = theTextarea.name;
      }
      new textEditor(theTextarea.id);
    }
  }
}

function textEditor(textareaID) {
  this.textarea = document.getElementById(textareaID);
  
  this.container = document.createElement("div");
  this.container.id = this.textarea.id + "-txtContainer";
  this.container.className = "-txtContainer";
  
  this.toolbar = new txtToolbar(this);

  this.textarea.id += "-txtTextarea";
  this.textarea.name += "-txtTextarea";

  this.container.appendChild(this.toolbar.theList);
  this.container.style.visibility = "hidden";
  
  this.textarea.parentNode.replaceChild(this.container, this.textarea);
}

function txtToolbar(theEditor) {
  this.txtEditorObject = theEditor;
    
  this.theList = document.createElement("ul");
  this.theList.id = this.txtEditorObject.textarea.id + "txtToolbar";
  this.theList.className = "txtToolbar";
  this.theList.txtToolbarObject = this;

  for (var i = 0; i < txtToolbarItems.length; i++) {
    switch (txtToolbarItems[i]) {
      case "bold":
        this.addButton(this.theList.id + "ButtonBold", "txtButtonBold", "Bold", "bold");
        break;
                
      case "link":
        this.addButton(this.theList.id + "ButtonLink", "txtButtonLink", "Hyperlink", "link");
        break;
    }
  }
}

txtToolbar.prototype.addButton = function(theID, theClass, theLabel, theAction) {
  var menuItem = document.createElement("li");
  var theLink = document.createElement("a");
  var theText = document.createTextNode(theLabel);
    
  menuItem.id = theID;
  menuItem.className = "txtEditButton";

  theLink.href = "#";
  theLink.title = theLabel;
  theLink.className = theClass;
  theLink.action = theAction;

  theLink.appendChild(theText);
  menuItem.appendChild(theLink);
  this.theList.appendChild(menuItem);
}

Als ik de DOM bekijk, krijg ik wel de DIV en de UL te zien, maar de textarea is nergens te vinden. Wat doe ik verkeerd?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • Clay
  • Registratie: Oktober 1999
  • Laatst online: 14-11 16:23

Clay

cookie erbij?

Je replacet de textarea voor de div en ul, line 33 in je codesnip. Die area moet je er dus wel weer bij zetten.

Instagram | Flickr | "Let my music become battle cries" - Frédéric Chopin


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Tip: removeChild geeft de node terug zodat je 'm weer ergens anders kan inserten ;)

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@crisp: als ik de tekstarea verwijder, kan ik er in de DOM niet meer naar refereren en dat geeft foutmeldingen:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  this.textarea = document.getElementById(textareaID);
  
  this.container = document.createElement("div");
  this.container.id = this.textarea.id + "-txtContainer";
  this.container.className = "-txtContainer";
  
  this.toolbar = new txtToolbar(this);

  this.textarea.id += "-txtTextarea";
  this.textarea.name += "-txtTextarea";
  this.newtextarea = this.textarea.parentNode.removeChild(this.textarea);
  
  this.container.appendChild(this.toolbar.theList);
  this.container.appendChild(this.newtextarea);
  this.container.style.visibility = "hidden";
  
  this.textarea.parentNode.appendChild(this.container); // foutmelding

Hoe kan ik er nu voor zorgen dat de container toch op de plaats van de originele textarea komt te staan? Is er geen manier om een DOM element te verplaatsen zonder het te verwijderen?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Voordat je removed een referentie naar de oorspronkelijke parent opslaan:
JavaScript:
1
2
3
4
5
6
7
8
9
10
var textarea = document.getElementById(textareaID);

var parent = textarea.parentNode;
var newtextarea = parent.removeChild(textarea);

this.container.appendChild(this.toolbar.theList);
this.container.appendChild(newtextarea);
this.container.style.visibility = "hidden";
  
parent.appendChild(this.container);

(sowieso snap ik niet waarom je overal properties van maakt ipv lokale variabelen, maak alleen properties van die referenties die je later nog nodig hebt).

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@crisp - ik heb de textEditor functie aangepast. Ik heb een paar dagen geleden een artikel gelezen over het gebruik van this in javascript, en ben misschien wat doorgeschoten :) Als ik een html document heb met 1 textarea, zoals uit de startpost, gaat alles goed. Ik krijg nu keurig een container met daarin een ul list en een textarea. Echter, als ik 2 textarea's op 1 pagina heb, wordt de dom een puinhoop.

[edit] De oplossing was om de call naar textEditor te vertragen via setTimeout. Blijkbaar heeft de DOM tijd nodig om aangepast te worden, en gaan aanpassingen door elkaar lopen zonder die tijdsbuffer...

[ Voor 79% gewijzigd door Reveller op 20-11-2006 22:05 ]

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

sowieso zit je met het feit dat appendChild altijd achteraan de parentNode toevoegd. Je kan dat op deze manier oplossen:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var textarea = document.getElementById(textareaID);

var parent = textarea.parentNode;
var nextsibling = textarea.nextSibling;
var newtextarea = parent.removeChild(textarea);

this.container.appendChild(this.toolbar.theList);
this.container.appendChild(newtextarea);
this.container.style.visibility = "hidden";

if (nextsibling)
    parent.insertBefore(this.container, nextsibling);
else
    parent.appendChild(this.container);

verder is de collectie die je terugkrijgt van getElementsByTagName() een live nodeList. Op het moment dat elementen fysiek van plaats veranderen werkt dat meteen door in je collectie en dat kan voor rare effecten zorgen. Als je dat wilt voorkomen zal je de referenties eerst los moeten opslaan, bijvoorbeeld in een array:
JavaScript:
1
2
3
4
var textareaCollection = new Array();
var textareas = document.getElementsByTagName('textarea');
for (var i = 0, l = textareas.length; i < l; i++)
    textareaCollection.push(textareas[i]);

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Een laatste vraag. Ik heb het gebruik van this teruggedrongen. Om een toolbar aan te maken, gebruik ik nog steeds de functies txtToolbar en addButton, welke leunen op this omdat ik een referentie naar theList nodig heb onderaan addButton. Kan dit dan ook zonder this worden opgelost en zo ja, hoe? Met behulp van een globale variabele?
JavaScript:
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
var toolbar = new txtToolbar(oldtextarea.id); // in textEditor()

function txtToolbar(id) {
  this.theList = document.createElement("ul");
  this.theList.id = id + "-txtToolbar";
  this.theList.className = "txtToolbar";

  for (var i = 0; i < txtToolbarItems.length; i++) {
    switch (txtToolbarItems[i]) {
      case "bold":
        this.addButton(this.theList.id + "ButtonBold", "txtButtonBold", "Bold", "bold");
        break;

      case "link":
        this.addButton(this.theList.id + "ButtonLink", "txtButtonLink", "Hyperlink", "link");
        break;

      case "image":
        this.addButton(this.theList.id + "ButtonImage", "txtButtonImage", "Insert Image", "image");
        break;
    }
  }
}

txtToolbar.prototype.addButton = function(theID, theClass, theLabel, theAction) {
  var menuItem = document.createElement("li");
  var theLink = document.createElement("a");
  var theText = document.createTextNode(theLabel);
    
  menuItem.id = theID;
  menuItem.className = "txtEditButton";

  theLink.href = "#";
  theLink.title = theLabel;
  theLink.className = theClass;
  theLink.action = theAction;

  theLink.appendChild(theText);
  menuItem.appendChild(theLink);
  this.theList.appendChild(menuItem);

  return true;
}

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Je kan theList natuurlijk ook gewoon als parameter doorgeven aan je method, het blijft immers een referentie. Pas als je later nog methods op je txtToolbar instance wilt aanroepen die een referentie naar theList nodig hebben zou ik er een instance property van maken.
Probeer global variables in ieder geval zoveel mogelijk te vermijden, zowel vanwege global namespace vervuiling als vanwege scope resolution performance.

Een imo mooi alternatief is zoiets:
JavaScript:
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
function txtToolbar(id)
{
    var theList = document.createElement("ul");
    theList.id = id + "-txtToolbar";
    theList.className = "txtToolbar";

    var button;
    for (var i = 0; i < txtToolbarItems.length; i++)
    {
        switch (txtToolbarItems[i])
        {
            case "bold":
                button = this.createButton(theList.id + "ButtonBold", "txtButtonBold", "Bold", "bold");
                break;
    
            case "link":
                button = this.createButton(this.theList.id + "ButtonLink", "txtButtonLink", "Hyperlink", "link");
                break;
    
            case "image":
                button = this.createButton(this.theList.id + "ButtonImage", "txtButtonImage", "Insert Image", "image");
                break;

            default:
                button = false;
        }

        if (button)
            theList.appendChild(button);
    }

    return theList;
}

txtToolbar.prototype.createButton = function(theID, theClass, theLabel, theAction)
{
    var menuItem = document.createElement("li");
    var theLink = document.createElement("a");
    var theText = document.createTextNode(theLabel);
    
    menuItem.id = theID;
    menuItem.className = "txtEditButton";

    theLink.href = "#";
    theLink.title = theLabel;
    theLink.className = theClass;
    theLink.action = theAction;

    theLink.appendChild(theText);
    menuItem.appendChild(theLink);

    return menuItem;
}

wel nog even duidelijk maken waar txtToolbarItems vandaan komt ;)

Verder is het ook de vraag of je hier ueberhaupt wel een geheel nieuw object voor nodig hebt, waarom zou je hier geen createToolbar() method van je textEditor object van maken?

[ Voor 65% gewijzigd door crisp op 20-11-2006 23:17 ]

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Sorry, maar nu raak ik de draad kwijt :) Voor de goede orde:
  • in init() maak ik een nieuw object aan, genaamd txtEditor
  • aan dit object hang ik in txtEditor() de property "toolbar", via de method createToolbar()
  • in createToolbar() gebruik je this.createButton om een nieuwe button te maken, omdat createButton een method is van txtEditor() en createToolbar() dat ook is
Dit alles moet gebeuren in de code hieronder. Toch krijg ik een error:
Error: uncaught exception: [Exception... "Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMHTMLDivElement.appendChild]" nsresult: "0x80004003 (NS_ERROR_INVALID_POINTER)" location: "JS frame :: file:///C:/Documents%20and%20Settings/Reveller/Desktop/texteditor/texteditor.js :: txtEditor :: line 34" data: no]
Waar in deze code maak ik nu de fout? Ik snap blijkbaar iets fundamenteels niet...
JavaScript:
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
var txtToolbarItems = ['bold', 'link', 'image']

window.onload = init;

function init() {
  var theTextareas = document.getElementsByTagName("textarea");
  for (var i = 0; i < theTextareas.length; i++) {
    var theTextarea = theTextareas[i];
    if (theTextarea.className.classExists("txtEditor")) {
      if (theTextarea.id == "") {
        theTextarea.id = theTextarea.name;
      }
      // voorlopige oplossing. TODO: oplossing crisp bekijken
      setTimeout("new txtEditor('"+ theTextarea.id +"')", 500 * (i));
    }
  }
}

function txtEditor(textareaID) {
  var oldtextarea = document.getElementById(textareaID);
  var parent = oldtextarea.parentNode;
  
  var container = document.createElement("div");
  container.id = oldtextarea.id + "-txtContainer";
  container.className = "txtContainer";
  
  this.toolbar = this.createToolbar(oldtextarea.id);

  oldtextarea.id += "-txtTextarea";
  oldtextarea.name += "-txtTextarea";
  
  var textarea = parent.removeChild(oldtextarea); 
  
  container.appendChild(this.toolbar.theList);
  container.appendChild(textarea); 
  
  parent.appendChild(container);

  if (textarea) {
    textarea.focus();
    detectKey(textarea);
    if (typeof textarea.createTextRange != 'undefined') {
      textarea.onkeyup = storeCursor;
      textarea.onclick = storeCursor;
      textarea.onselect = storeCursor;
      textarea.onselect();
    }
  }
}

txtEditor.prototype.createToolbar = function(id) {
  var theList = document.createElement("ul");
  theList.id = id + "-txtToolbar";
  theList.className = "txtToolbar";

  var button;
  for (var i = 0; i < txtToolbarItems.length; i++) {
    switch (txtToolbarItems[i]) {
      case "bold":
        button = this.createButton(theList.id + "ButtonBold", "txtButtonBold", "Bold", "bold");
        break;
    
      case "link":
        button = this.createButton(theList.id + "ButtonLink", "txtButtonLink", "Hyperlink", "link");
        break;
    
      case "image":
        button = this.createButton(theList.id + "ButtonImage", "txtButtonImage", "Insert Image", "image");
        break;

      default:
        button = false;
    }
  }
  
  if (button) {
    theList.appendChild(button);
  }

  return theList;
}

txtEditor.prototype.createButton = function(theID, theClass, theLabel, theAction) {
  var menuItem = document.createElement("li");
  var theLink = document.createElement("a");
  var theText = document.createTextNode(theLabel);
    
  menuItem.id = theID;
  menuItem.className = "txtEditButton";

  theLink.href = "#";
  theLink.title = theLabel;
  theLink.className = theClass;
  theLink.action = theAction;

  theLink.appendChild(theText);
  menuItem.appendChild(theLink);

  return menuItem;
}

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

JavaScript:
1
2
3
this.toolbar = this.createToolbar(oldtextarea.id);
// ...
container.appendChild(this.toolbar);

of
JavaScript:
1
2
3
var toolbar = this.createToolbar(oldtextarea.id);
// ...
container.appendChild(toolbar);
;)

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@crisp - beide mogelijkheden had ik al geprobeerd, maar ze leiden beiden tot genoemde error. Voor de duidelijkheid hieronder de hele code. Ziet iemand waar het verkeerd gaat? Ik weet het echt niet meer :)


JavaScript:
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
var txtToolbarItems = ['bold', 'link', 'image']

window.onload = init;

function init() {
  var theTextareas = document.getElementsByTagName("textarea");
  for (var i = 0; i < theTextareas.length; i++) {
    var theTextarea = theTextareas[i];
    if (theTextarea.className.classExists("txtEditor")) {
      if (theTextarea.id == "") {
        theTextarea.id = theTextarea.name;
      }
      setTimeout("new txtEditor('"+ theTextarea.id +"')", 500 * (i));
    }
  }
}

function txtEditor(textareaID) {
  var oldtextarea = document.getElementById(textareaID);
  var parent = oldtextarea.parentNode;
  
  var container = document.createElement("div");
  container.id = oldtextarea.id + "-txtContainer";
  container.className = "txtContainer";
  
  var toolbar = this.createToolbar(oldtextarea.id);
  
  oldtextarea.id += "-txtTextarea";
  oldtextarea.name += "-txtTextarea";
  
  var textarea = parent.removeChild(oldtextarea); 
  
  container.appendChild(toolbar.theList);
  container.appendChild(textarea); 
  
  parent.appendChild(container);
}

txtEditor.prototype.createToolbar = function(id) {
  var theList = document.createElement("ul");
  theList.id = id + "-txtToolbar";
  theList.className = "txtToolbar";

  var button;
  for (var i = 0; i < txtToolbarItems.length; i++) {
    switch (txtToolbarItems[i]) {
      case "bold":
        button = this.createButton(theList.id + "ButtonBold", "txtButtonBold", "Bold", "bold");
        break;
    
      case "link":
        button = this.createButton(theList.id + "ButtonLink", "txtButtonLink", "Hyperlink", "link");
        break;
    
      case "image":
        button = this.createButton(theList.id + "ButtonImage", "txtButtonImage", "Insert Image", "image");
        break;

      default:
        button = false;
    }
  }
  
  if (button) {
    theList.appendChild(button);
  }

  return theList;
}

txtEditor.prototype.createButton = function(theID, theClass, theLabel, theAction) {
  var menuItem = document.createElement("li");
  var theLink = document.createElement("a");
  var theText = document.createTextNode(theLabel);
    
  menuItem.id = theID;
  menuItem.className = "txtEditButton";

  theLink.href = "#";
  theLink.title = theLabel;
  theLink.className = theClass;
  theLink.action = theAction;

  theLink.appendChild(theText);
  menuItem.appendChild(theLink);

  return menuItem;
}

String.prototype.addClass = function(theClass) {
  if (this != "") {
    if (!this.classExists(theClass)) {
      return this + " " + theClass;
    }
  }
  else {
    return theClass;
  }
  return this;
}

String.prototype.classExists = function(theClass) {
  var regString = "(^| )" + theClass + "\W*";
  var regExpression = new RegExp(regString);

  if (regExpression.test(this)) {
    return true;
  }
  return false;
}

String.prototype.removeClass = function(theClass) {
  var regString = "(^| )" + theClass + "\W*";
  var regExpression = new RegExp(regString);

  return this.replace(regExpression, "");
}

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Persoonlijk zou ik het ongeveer zo doen:
JavaScript:
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
function init()
{
    var theTextareas = document.getElementsByTagName('textarea');
    var thisTextarea, i = 0;
    while ((thisTextarea = theTextareas[i++]))
    {
        if (thisTextarea.className.classExists('txtEditor'))
        {
            if (thisTextarea.id == '')
                thisTextarea.id = thisTextarea.name;

            new txtEditor(thisTextarea);
        }
    }
}

function txtEditor(textarea)
{
    var parent = textarea.parentNode;
    var nextsibling = textarea.nextSibling;

    var container = document.createElement('div');
    container.id = textarea.name + '-txtContainer';
    container.className = 'txtContainer';
  
    var toolbar = this.createToolbar(textarea.name);
    var newtextarea = parent.removeChild(textarea); 

    container.appendChild(toolbar);
    container.appendChild(newtextarea); 

    if (nextsibling)
        parent.insertBefore(container, nextsibling);
    else
        parent.appendChild(container);
}

txtEditor.prototype.createToolbar = function(idprefix)
{
    var theList = document.createElement('ul');
    theList.id = idprefix + '-txtToolbar';
    theList.className = 'txtToolbar';

    var button;
    for (var i = 0; i < txtToolbarItems.length; i++)
    {
        switch (txtToolbarItems[i])
        {
            case 'bold':
                button = this.createButton(theList.id + 'ButtonBold', 'txtButtonBold', 'Bold', 'bold');
                break;
    
            case 'link':
                button = this.createButton(theList.id + 'ButtonLink', 'txtButtonLink', 'Hyperlink', 'link');
                break;
    
            case 'image':
                button = this.createButton(theList.id + 'ButtonImage', 'txtButtonImage', 'Insert Image', 'image');
                break;

            default:
                button = false;
        }

        if (button)
            theList.appendChild(button);
    }

    return theList;
}

txtEditor.prototype.createButton = function(theID, theClass, theLabel, theAction)
{
    var menuItem = document.createElement('li');
    var theLink = document.createElement('a');
    var theText = document.createTextNode(theLabel);
    
    menuItem.id = theID;
    menuItem.className = 'txtEditButton';

    theLink.href = '#';
    theLink.title = theLabel;
    theLink.className = theClass;
    theLink.action = theAction;

    theLink.appendChild(theText);
    menuItem.appendChild(theLink);

    return menuItem;
}

;)

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
crisp - dank je wel! Hierdoor kom ik weer een stuk verder. Nu heb ik slechts nog 1 laatste vraag:
  • op het moment dat er in regel 12 een nieuw txtEditor object wordt aangemaakt, moet ik hier properties aan kunnen toevoegen, bijvoorbeeld:
    JavaScript:
    1
    2
    3
    4
    5
    
    function txtEditor(textarea) {
      this.doel = textarea.name;
      var parent = textarea.parentNode;
      var nextsibling = textarea.nextSibling;
      // ... etc
    zoals je ziet heb ik de property "doel" toegekend aan object txtEditor
  • ik heb de createButton functie uitgebreid met een onclick event:
    JavaScript:
    1
    
      theLink.onclick = this.toolbarAction;
  • de functie toolbarAction is als volgt:
    JavaScript:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    txtEditor.prototype.toolbarAction = function() {
      var target = txtEditor.doel;
    
      if (typeof target.cursorPos != 'undefined') {
        IEWrap(type, text);
      }
      else if (typeof target.selectionStart != 'undefined') {
        mozWrap(type, text);
      }
      else {
        defaultWrap(type, text);
      }
    }
    Ik had gedacht dat ik, omdat txtEditor een object is, de variabele "target" nu als waarde textarea.name uit de txtEditor() functie zou meekrijgen. Echter, de Javascript console van FF geeft als error: "txtEditor.name is undefined"
Waarom kan ik niet in elke functie de properties van txtEditor aanroepen?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

'this' binnen je onclick-handler refereert naar je anchor-element. Je zal gebruik moeten maken van closures als je die scope wilt aanpassen. Ik denk dat je hiermee wel uit de voeten kan:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function txtEditor(textarea)
{
    var parent = textarea.parentNode;
    var nextsibling = textarea.nextSibling;

    var container = document.createElement('div');
    container.id = textarea.name + '-txtContainer';
    container.className = 'txtContainer';
  
    this.textarea = parent.removeChild(textarea); 
    var toolbar = this.createToolbar(textarea.name);

    container.appendChild(toolbar);
    container.appendChild(this.textarea); 

    if (nextsibling)
        parent.insertBefore(container, nextsibling);
    else
        parent.appendChild(container);
}

Hier maak ik de referentie naar de textarea expliciet een property van de txtEditor instance
JavaScript:
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
txtEditor.prototype.createButton = function(theID, theClass, theLabel, theAction)
{
    var menuItem = document.createElement('li');
    var theLink = document.createElement('a');
    var theText = document.createTextNode(theLabel);

    menuItem.id = theID;
    menuItem.className = 'txtEditButton';

    theLink.href = '#';
    theLink.title = theLabel;
    theLink.className = theClass;
    theLink.action = theAction;

    // scoping
    var self = this;
    var onclickHandler = this.toolbarAction;
    theLink.onclick = function()
    {
        onclickHandler.call(self, theAction);
    }

    theLink.appendChild(theText);
    menuItem.appendChild(theLink);

    return menuItem;
}

txtEditor.prototype.toolbarAction = function(theAction)
{
    // 'this' refers to the txtEditor instance
    alert(this.textarea.id);
    alert(theAction);
}

Hier maak ik een closure aan waarbij ik de toolbarAction functie expliciet uit laat voeren in de scope van je txtEditor instance. Aangezien de referentie naar je textarea daar weer een property van is kan je die in je handler weer eenvoudig opvragen :)

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Crisp, dank je voor je hulp! Ik begin al echt een goed werkende editor te krijgen :) Ik kan nu tekst selecteren en er bold tags omheen zetten, een link invoeren en bold tags verwijderen. Als alles goed werkt, en er meer functies inzitten, zet ik de applicatie online.

Zojuist wilde ik een functie bouwen zodat ik de buttons uit de toolbar "aan" kan zetten als de cursor op een woord staat dat bijvoorbeeld omgeven is door "bold" tags. Met andere woorden:
code:
1
2
3
4
5
6
[[b]] [u] [i] [url] [img]
+----------------------------------------+
|dit is een <b>b|old</b> stukje tekst... |
|                                        |
|                                        |
+----------------------------------------+

In dit voorbeeld staat de cursor op het woordje "bold" (tussen de b en de o). De [b] button uit de toolbar is gehighlight (aangegeven met dubbele haken).

Om dit te bewerkstelligen, heb ik een even toegevoegd in de txtEditor functie:
JavaScript:
1
2
3
4
5
  var self = this;
  var onkeyupHandler = this.checkToolbarState;
  target.onkeyup = function() {
    onkeyupHandler.call(self);
  }

Hierdoor wordt op elke keyup (en later ook elke mouseup) de toolBarState aangepast. Om de state (lees: css class) van de toolbar buttons aan te kunnen passen, moet ik de beschikking hebben over de "theList" variabele uit de createToolbar functie, zodat ik in checkToolbarState() bv. het volgende kan doen:
JavaScript:
1
2
3
4
var menuListItems = this.toolbar.theList.childNodes; // "undefined"
for (var i = 0; i < menuListItems.length; i++) {
  menuListItems[i].className = menuListItems[i].className.removeClass("on");
}

Met andere woorden: alle buttons "uit" zetten, om daarna (via document.selection / getSelection) te kijken door welke tags een woord / selectie omgeven wordt en dus welke buttons weer "on" moeten worden gezet.

Het probleem: hoe haal ik de list met toolbar buttons op? this.toolbar.theList.childNodes geeft "undefined"...

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Je zou je toolbar(-items) dan ook properties van je object-instance moeten maken

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
@crisp - waarom werkt onderstaande niet? Ik had verwacht dat ik met deze aanpassingen this.toolbar.theList zou kunnen oproepen met daarin alle li elementen:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// in de functie txtEditor (waarin een nieuwe instance wordt aangemaakt):
this.toolbar = this.createToolbar(textarea.name);

// in de functie createToolbar:
this.theList = document.createElement('ul');
this.theList.id = idprefix + '-txtToolbar';
this.theList.className = 'txtToolbar';

button = this.createButton(this.theList.id + 'ButtonBold', ...) // etc..

//...

if (button) {
  this.theList.appendChild(button);
}

//...

return this.theList;

De teksteditors worden wel aangemaakt, maar this.toolbar.theList is undefined. Ik snap niet waarom, want this.theList wordt toch netjes gereturned?

edit:
Laat maar! Ik moet natuurlijk this.toolbar.childNodes oproepen :)

[ Voor 4% gewijzigd door Reveller op 26-11-2006 01:03 ]

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Nog een vraag. Dit werkt perfect:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
txtEditor.prototype.detectKey = function() {
  if (document.all) {
    this.textarea.onkeydown = function(e) { return handleKey(e); };
  }
  else {
    this.textarea.onkeypress = function(e) { return handleKey(e); };
  }
}

function handleKey(e) {
  // window.event afvangen...
}

Maar als ik handleKey onderdeel wil maken van de txtEditor class, krijg ik de melding "this.handleKey is not defined":
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
txtEditor.prototype.detectKey = function() {
  if (document.all) {
    this.textarea.onkeydown = function(e) { return this.handleKey(e); };
  }
  else {
    this.textarea.onkeypress = function(e) { return this.handleKey(e); };
  }
}

txtEditor.prototype.handleKey = function(e) {
  // afvangen...
}

Wat doe ik verkeerd?
edit:
de editor begint overigens al redelijk vorm te krijgen :) Als alle features erin zitten die ik erin wil hebben, zet ik de source weer online

[ Voor 9% gewijzigd door Reveller op 26-11-2006 23:28 ]

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

JavaScript:
1
this.textarea.onkeydown = this.handleKey;

should work...

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Werkt perfect :) Alleen nu het volgende: met onderstaande code vang ik een CTRL-B af. De bedoeling is om dezelfde bewerking uit te voeren als wanneer iemand op het "bold" knopje in de toolbar klikt. Daarom roep ik this.toolbarAction aan in regel 14. Ik krijg dan de error "this.toolbarAction is not a function". Ik snap niet waarom niet, omdat handleKey en toolbarAction allebei functies van de class txtEditor zijn (toch?). Als ik er this.toolbarAction.call('bold') van maak, krijg ik de error "this.toolbarAction has no properties". Hoe moet ik nu toolbarAction aanroepen en waar kan ik vinden welke type call ik in welke situatie moet maken?
JavaScript:
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
txtEditor.prototype.handleKey = function(e) {
  if (!e || e == null) var e = window.event;
  if (e.keyCode) key = e.keyCode;
  else if (e.which) key = e.which - 32;

  if (key == 9 && (getCookie('tabs') == 1)) {
    //wrapSelection('plain', '  ');
    return false;
  }

  if (e.ctrlKey && !e.shiftKey) {
    switch (key) {
      case 66:
        this.toolbarAction('bold');
        return cancelEvent(e);
        break;
    }
  }
  return true;
}

txtEditor.prototype.toolbarAction = function(theAction) {
  var target = this.textarea;
  
  if (typeof target.cursorPos != 'undefined') {
    this.IEWrap(theAction);
  }
  else if (typeof target.selectionStart != 'undefined') {
    this.mozWrap(theAction);
  }
  else {
    defaultWrap(theAction);
  }
}

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

Omdat handleKey wordt uitgevoerd binnen de context van je textarea Element en 'this' dus niet verwijst naar je object instance. Kortom: weer scopen dmv een closure zoals ik al eerder heb voorgedaan :)

Ik krijg een beetje het idee dat je dat concept van scopes nog niet helemaal doorhebt, dus wellicht is het handig om daar eens op te googlen ;)

Intentionally left blank


  • Reveller
  • Registratie: Augustus 2002
  • Laatst online: 05-12-2022
Er zijn heel veel artikelen te vinden over scoping in javascript. Ik vond er eentje die de lading goed dekte en denk dat ik snap wat ik hier moet doen. Een poging tot uitleg:

Het probleem is, dat handleKey aangeroepen wordt door het textarea object (via onkeydown (IE) en onkeypress (FF)) en dat this binnen het die handler dus verwijst naar de textarea en niet naar het txtEditor object. Ik moet dus binnen de detectKey functie (waar deze handlers worden gedefinieerd) een closure aanmaken, waarbij ik de handleKey functie betrek in de scope van de txtEditor instantie, in plaats van het textarea object / instantie.

In de functie txtEditor (welke een nieuwe instantie van het object aanmaakt):
JavaScript:
1
2
3
4
5
function txtEditor(textarea) {
  // ...
  this.detectKey();
  // ...
}


In de functie detectKey:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
txtEditor.prototype.detectKey = function() {
  var self = this; // scoping txtEditor object
  var theHandler = this.handleKey;
  
  if (document.all) {
    this.textarea.onkeydown = function() { theHandler.call(self); }
  }
  else {
    this.textarea.onkeypress = function() { theHandler.call(self); }
  }
}

Hierdoor verwijst this in handleKey naar de editor instantie :)

Ik krijg nu alleen de volgende error: "e has no properties". Als ik hierboven "theHandler.call(self, e)" ervan maak, krijg ik een "e is undefined" error, ook als ik "var e = window.event" aan detectKey toevoeg. Voor de volledigheid de handleKey functie:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
txtEditor.prototype.handleKey = function(e) {
  if (!e || e == null) var e = window.event;
  if (e.keyCode) key = e.keyCode;
  else if (e.which) key = e.which - 32;

  if (key == 9 && (getCookie('tabs') == 1)) {
    // do stuff...
    return false;
  }

  if (e.ctrlKey && !e.shiftKey) {
    switch (key) {
      case 66:
        // do stuff...
        return cancelEvent(e);
        break;
    }
  }
  return true;
}

Hoe los ik dit nu weer op?

"Real software engineers work from 9 to 5, because that is the way the job is described in the formal spec. Working late would feel like using an undocumented external procedure."


  • crisp
  • Registratie: Februari 2000
  • Nu online

crisp

Devver

Pixelated

JavaScript:
1
this.textarea.onkeydown = function(e) { theHandler.call(self, e); }
;)

Intentionally left blank


  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

Ehm, wellicht offtopic, maar wie is nu precies je editor aan 't bouwen? :P

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.

Pagina: 1