0

Old time OO programmer, new to javascript/html coding.

So I'm playing around with JavaScript "classes", trying to make a reusable unit that writes itself into the html.

<html>
<script>
function Foo(min_input, max_input) {
    var n_min_input = min_input;
    var n_input = max_input;
    var curr_shown = min_input;
    this.show_one_more = function () {
        if (curr_shown < n_input) {
            document.getElementById('input' + curr_shown).style = 'display: block';
            curr_shown++;
        }
    }
    this.show_one_less = function () {
        if (curr_shown > n_min_input) {
            curr_shown--;
            document.getElementById('input' + curr_shown).style = 'display: none';
        }
    }
    this.write_inputs = function () {
        for (var i = 0; i < n_min_input; i++) {
            document.write("<input type='text' style='display: block' value='" + i + "' id='input" + i + "' name='input" + i + "'/>");
        }
        for (var i = n_min_input; i < n_input; i++) {
            document.write("<input type='text' style='display: none' value='" + i + "' id='input" + i + "' name='input" + i + "'/>");
        }
        //want to essentially do this, but don't know how
        document.write("<p><input type='button' onclick='this.show_one_more()' value='+'></input><input type='button' value='-' onclick='this.show_one_less()'/></p>");
    }
}
var f = new Foo(1, 5);
f.write_inputs();
//works here, but would rather it be generated in f.write_inputs();
document.write("<p><input type='button' onclick='this.show_one_more()' value='+'></input><input type='button' value='-' onclick='this.show_one_less()'/></p>");
</script>
<input type="button" value="stuff" id="sbutton"/>
</html>

So basically, i want to get some sort of closure with this in the document write call on the button. I feel like this is a pattern people may have used in the past (but I could be totally wrong). My ideas:

make a global variable var foo_idx=0 and an array var allfoos = [];. In the initialize (body of the class) do this.foo_idx=foo_idx; allfoos[foo_idx]=this;foo_idx++ and then I can use allfoos[foo_idx] to capture this. BUT that feels hacky. How do people typically create reusable html elements?

j08691
  • 204,283
  • 31
  • 260
  • 272
IdeaHat
  • 7,641
  • 1
  • 22
  • 53
  • 5
    First and most important thing: __DROP__ `document.write`. It can only be used to write stuff into the current document at page load time – any call to it afterwards (like from within a function) _overwrites_ the current document. Go read up on how to create elements using DOM methods, or use `.innerHTML`, or – don’t re-invent the wheel, but use a library instead that has more sophisticated DOM manipulation methods on board already. – CBroe Mar 03 '14 at 22:07

2 Answers2

1

Unless I misunderstand your need here, just save it to a variable:

function Foo(min_input, max_input) {
    // save a reference to "this" to be used in anonymous function
    var that = this; 

    this.show_one_more = function () {
        // can use "that" here to reference the outer "this"
    }
}

Re-reading your code comments, you would need to ditch document.write (and probably should anyway) and work directly with the dom elements, not a string. Then you could do something like this:

var input = document.createElement("input");
input.type = "button";
input.value = "+";

// on click, call the outer object's function
input.onclick = that.show_one_more;

container.appendChild(input);

Demo of the basic concept: http://jsfiddle.net/SaLvE/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
0

You would be better off binding callback functions to the onclick handler of the elements you are creating. It is generally considered an anti pattern to write out explicit event handlers on HTML tags.

Also, you could have references to these DOM objects you create if you created actual DOM objects instead of using document.write to write out html as a string.

Here is something along the lines of what I'm suggesting that would go in place of the line you commented as //want to essentially do this:

//You will need a parent DOM element to insert into. Defaulting to <body> (your example is actually missing this basic html tag)
var parentContainer = document.getElementsByTagName('body')[0];

var newParagraph = document.createElement('p');
var newButton1 = document.createElement('input');
var newButton2 = document.createElement('input');

newButton1.type='button'
newButton2.type='button'

newButton1.value='+'
newButton2.value='-'

newButton1.addEventListener('click', this.show_one_more,false);
newButton2.addEventListener('click', this.show_one_less,false);

newParagraph.appendChild(newButton1);
newParagraph.appendChild(newButton2);
parentContainer.appendChild(newParagraph);

While not relevant to your show_one_more and show_one_less functions, it is worth pointing out that in this context the value this inside of those functions will end up resolving to the DOM element for the button and not the Foo object instance.

This is because the DOM element becomes the caller of the event handler and the call to the onclick callback, thus becoming this. If you wanted to avoid this and have this still reference the Foo object instance, then you would use the bind function as follow:

newButton1.addEventListener('click', this.show_one_more.bind(this),false);
newButton2.addEventListener('click', this.show_one_less.bind(this),false);
Rich
  • 2,805
  • 8
  • 42
  • 53