Peregrine
Peregrine

Reputation: 367

Event Handlers On Newly Created Elements

I have a web app with a number of textareas and the ability to add more if you wish.

When you shift focus from one textarea to another, the one in focus animates to a larger size, and the rest shrink down.

When the page loads it handles the animation perfectly for the initial four boxes in the html file, but when you click on the button to add more textareas the animation fails to accomodate these new elements... that is, unless you place the initial queries in a function, and call that function from the addelement function tied to the button.

But!, when you do this it queries as many times as you add a new element. So, if you quickly add, say 10, new textareas, the next time you lay focus on any textarea the query runs 10 times.

Is the issue in my design, or jQueries implementation? If the former, how better can I design it, if it is the latter, how can I work around it?

I've tried to chop the code down to the relevant bits... I've tried everything from focus and blur, to keypresses, the latest is on click.

html::

<html>
<head>
    <link rel="stylesheet" type="text/css" href="./sty/sty.css" />      
    <script src="./jquery.js"></script>
<script>
    $().ready(function() {
        var $scrollingDiv = $("#scrollingDiv");

        $(window).scroll(function(){            
            $scrollingDiv
                .stop()
                //.animate({"marginTop": ($(window).scrollTop() + 30) + "px"}, "slow" );            
                .animate({"marginTop": ($(window).scrollTop() + 30) + "px"}, "fast" );          
        });
    });
</script>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>boxdforstacks</title>
</head>
<body>
    <div class="grid">

        <div class="col-left" id="left">
                        <div class="module" id="scrollingDiv">
            <input type="button" value="add" onclick="addele()" />
            <input type="button" value="rem" onclick="remele()" />
            <p class="display">The value of the text input is: </p>
            </div>
      </div> <!--div class="col-left"-->


        <div class="col-midd">
            <div class="module" id="top">
                            <p>boxa</p>
                            <textarea class="tecksd" placeholder="begin typing here..." id="boxa" ></textarea>
                            <p>boxb</p>
                            <textarea class="tecksd" placeholder="begin typing here..." id="boxb"></textarea>
                            <p>boxc</p>
                            <textarea class="tecksd" placeholder="begin typing here..." id="boxc"></textarea>
                            <p>boxd</p>
                            <textarea class="tecksd" placeholder="begin typing here..." id="boxd"></textarea>
      </div>
        </div> <!--div class="col-midd"-->
    </div> <!--div class="grid"-->

</body>
</html>
    <script type="text/javascript" src="boxd.js"></script> 

js:

function onit(){
    $('textarea').on('keyup change', function() {
        $('p.display').text('The value of the text input is: ' + $(this).val());
    });

}
$('textarea').on("click",function(){
        //alert(this.id.substring(0,3));
        if ( this.id.substring(0,3)  == 'box' ){
                        $('textarea').animate({ height: "51" }, 1000);
                        $(this).animate({ height: "409" }, 1000);
        } else {
                        $('textarea').animate({ height: "51" }, 1000);
        }
}
    );
var boxfoc="";
var olebox="";
var numb = 0;
onit();

function addele() {

  var tops = document.getElementById('top');
    var num = numb + 1;
    var romu = romanise(num);

  var newbox = document.createElement('textarea');
  var newboxid = 'box'+num;

  newbox.setAttribute('id',newboxid);
    newbox.setAttribute('class','tecksd');
    newbox.setAttribute('placeholder','('+romu+')');

  tops.appendChild(newbox);

    numb = num;
    onit();
} //addele(), add element




function remele(){

  var tops = document.getElementById('top');
  var boxdone = document.getElementById(boxfoc);
  tops.removeChild(boxdone);
} // remele(), remove element



function romanise (num) {
    if (!+num)
        return false;
    var digits = String(+num).split(""),
        key = ["","c","cc","ccc","cd","d","dc","dcc","dccc","cm",
               "","x","xx","xxx","xl","l","lx","lxx","lxxx","xc",
               "","i","ii","iii","iv","v","vi","vii","viii","ix"],
        roman = "",
        i = 3;
    while (i--)
        roman = (key[+digits.pop() + (i * 10)] || "") + roman;
    return Array(+digits.join("") + 1).join("M") + roman;
} // romanise(), turn numbers into roman numerals

css :

.tecksd {
    width: 97%;
    height: 51;
    resize: none;
    outline: none;
    border: none;
    font-family: "Lucida Console", Monaco, monospace;
    font-weight: 100;
    font-size: 70%;
    background: white;
/*  box-shadow: 1px 2px 7px 1px #0044FF;*/
}

.tecksded {
    width: 97%;
    resize: none;
    outline: none;
    border: none;
    overflow: auto;
    position: relative;
    font-family: "Lucida Console", Monaco, monospace;
    font-weight: 100;
    font-size: 70%;
    background: white;
/*  box-shadow: 1px 2px 7px #FFDD00;*/
}


/*#postcomp {
    width: 500px;
}*/


* {
  @include box-sizing(border-box);
}

$pad: 20px;

.grid {
  background: white;
  margin: 0 0 $pad 0;

  &:after {
    /* Or @extend clearfix */
    content: "";
    display: table;
    clear: both;
  }
}

[class*='col-'] {
    float: left;
  padding-right: $pad;
  .grid &:last-of-type {
    padding-right: 0;
  }
}
.col-left {
    width: 13%;
}
.col-midd {
    width: 43%;
}
.col-rght {
    width: 43%;
}

.module {
  padding: $pad;


}

/* Opt-in outside padding */
.grid-pad {
  padding: $pad 0 $pad $pad;
  [class*='col-']:last-of-type {
    padding-right: $pad;
  }
}

body {
    padding: 10px 50px 200px;
  background: #FFFFFF;
  background-image: url('./backgrid.png');

}
h1 {
  color: black;
    font-size: 11px;
    font-family: "Lucida Console", Monaco, monospace;
    font-weight: 100;
}
p {
  color: white;
    font-size: 11px;
    font-family: "Lucida Console", Monaco, monospace;
    font-weight: 100;
}

Upvotes: 0

Views: 113

Answers (3)

Sam Baumgarten
Sam Baumgarten

Reputation: 2259

The issue is you are binding the textareas only on the page load. I made a JSFiddle with working code: http://jsfiddle.net/VpABC/

Here's what I changed: I wrapped:

$('textarea').on("click", function () {
        //alert(this.id.substring(0,3));
        if (this.id.substring(0, 3) == 'box') {
            $('textarea').animate({
                height: "51"
            }, 1000);
            $(this).animate({
                height: "409"
            }, 1000);
        } else {
            $('textarea').animate({
                height: "51"
            }, 1000);
        }
    });

in a function so it looked like this:

function bindTextAreas() {
    $('textarea').unbind("click");
    $('textarea').on("click", function () {
        //alert(this.id.substring(0,3));
        if (this.id.substring(0, 3) == 'box') {
            $('textarea').animate({
                height: "51"
            }, 1000);
            $(this).animate({
                height: "409"
            }, 1000);
        } else {
            $('textarea').animate({
                height: "51"
            }, 1000);
        }
    });
}
bindTextAreas();

What this does is it allows you to call this function, bindTextAreas, whenever you create a new textarea. This will unbind all the current events than rebind them. This will make it so your new textarea is has the click handler setup.

An place where this function is called is in the addele function like this:

function addele() {

    var tops = document.getElementById('top');
    var num = numb + 1;
    var romu = romanise(num);

    var newbox = document.createElement('textarea');
    var newboxid = 'box' + num;

    newbox.setAttribute('id', newboxid);
    newbox.setAttribute('class', 'tecksd');
    newbox.setAttribute('placeholder', '(' + romu + ')');

    tops.appendChild(newbox);

    numb = num;
    onit();
    bindTextAreas();
} //addele(), add element

Notice the bindTextAreas(); line near the bottom. This reloads all the click handlers.

Upvotes: 0

palaѕн
palaѕн

Reputation: 73926

You should use the following:

// New way (jQuery 1.7+) - .on(events, selector, handler)
$(document).on("click", "textarea", function () {
    event.preventDefault();
    alert('testlink'); 
});

Since the textarea is added dynamically, you need to use event delegation to register the event handler.

Upvotes: 2

GijsjanB
GijsjanB

Reputation: 10044

Try

$(document).on('click', 'textarea', function() {
    // do something
});

Upvotes: 1

Related Questions