JQuery: dynamically update HTML so that it is recognized by JQuery - javascript

So, first of all, I am a newbie in Web (html/js)
What I want to achieve:
I have a custom tree and I want to be able to dynamically (using jquery) create children for that tree:
Html:
<ul>
<li>
Item
<input type="button" value="+" class="childAdder">
<ul class="childrenList"></ul>
</li>
</ul>
JS:
$(".childAdder").click(function() { // add child which is the same as root
$(this).parent().children(".childrenList").append(
'<li>Item</li>\
<input type="button" value="+" class="childAdder">\
<ul class="childrenList"></ul>'
);
});
And as you can see, I know how to add a child (more or less, an advice is always welcomed). The problem is, however, that this code only works for items that are "predefined" in html --> everytime I dynamically(via JS) add a child, this code just does not execute for this newly created element (tried to do alert('Hello'); - nothing is seen)
Question:
I assume I need to somehow "properly" add my new child to the DOM(?) of HTML or whatever, so that it is then recognized by JS, right? (but that seems to be only achieved trough HTML static page, no?)
Anyway, how do I make this thing work: add new child so that the same JS code that is executed for HTML element is executed for the element created by JS
is there a solution or the whole implementation is just wrong?

You need to use event delegation for this to work. This is done by using the on JQuery method for event wiring and modifying the parameters.
Also (FYI), a <ul> element can only contain <li> elements as children. Your bullet/nested list structure is invalid.
Lastly, in the HTML string you were appending included random \ characters, which are not needed and are actually invalid at those locations.
Here's the correct implementation with the HTML corrected to be valid.
// JQuery event delegation requires the use of the "on" method and then you
// bind the event to an object that is sure to always recieve the event (document
// works well here because of event bubbling). Then you specify the target element
// that the actual event will be triggered from.
$(document).on("click", ".childAdder" ,function() {
// add child which is the same as root
$(this).parent().children(".childrenList").append("<li>Item<br><input type='button' value='+' class='childAdder'><ul class='childrenList'></ul></li>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<!-- <ul> and <ol> elements can only have <li> elements as thier children (aside from
comments like this one). To properly nest lists, organize the HTML as follows: -->
<li>Item<br>
<input type="button" value="+" class="childAdder">
<ul class="childrenList"></ul>
</li>
</ul>

Related

Angular and jQuery: Events not attached to dynamically created HTML elements

I am using jQuery to dynamically create new elements in an Angular Form. The form is built using Template Driven Forms approach. The dynamic elements are successfully created but they are not assigned events/callbacks apparently because the component was already compiled and did not re-compile for the dynamic elements. This keeps the new elements from reporting data or responding despite that the name attribute and ngModel directive is assigned to it. How do I go about this? I have to read the form data for storing in database.
The TypeScript File code which generates the new component's HTML is as under (do not focus on class/ID names etc for I modified them for simplifying question statement). The function uses a counter for assigning unique name to the new input element.
private counter = 0;
increaseElementDynamically(){
this.counter++;
var htmlTagDef = '<input #benchReff'+this.counter.toString()+' ="ngModel" type="text" class=" form-control mb-3" id="bench'+this.counter.toString()+'" required name="bench'+this.counter.toString()+'" ngModel>';
$("#myDiv").append(htmlTagDef);
console.log(htmlTagDef);
}
The input element is written as under in the component's HTML file
<div class="form-group col-lg-3 border-right border-primary">
<label for="benchGroup">Bench Members</label>
<div ngModelGroup="benchGroup">
<div id="customDiv">
<input #benchReff ="ngModel" type="text" class=" form-control mb-3" id="bench" required name="bench" ngModel>
</div>
</div>
<button type="button" class="btn btn-primary float-right m-3" (click)="iincreaseElementDynammically()">Add</button>
</div>
Even if a simple click event is assigned, it wont work for the dynamically created elements since they were created on runtime.
How can the proper dynamic behavior be achieved with functionality?
you need to attach click event or any other event handler using parent element delegation.
$('#myDiv').on('click','#bench', function(){ // will be triggered on click of bench input which is added dynamically
console.log('clicked');
});
Solution: jQuery was operating on front end whereas the component was recompiling. Better approach was to eliminate jQuery and use ngFor directive. For new [dynamic] elements, new IDs can be pushed to an array with ngFor operating on same array to dynamically create elements which fully supported Angular functionality.

Remove parent element with Javascript using parentNode.removeChild when I only have 1 parent?

I'm working on a project where I have to delete en element in a certain condition (if). My code generates a <li> in which data is inserted via an array. I need to delete that <li> if some conditions are met.
Since removeParent() doesn't exist I've found different methods saying to use
e.parentNode.parentNode.removeChild(e.parentNode);
Rings a bell, it works in certains cases of course.
BUT
In my case, this is dynamically rendered elements in an array inside the <li> and there is no parent of the parent, so I get an error.
My script looks for an element in the <li>, in my test I use a <div> found by its class.
$(node).find('.results-name')[0].parentNode.remove();
Does NOT work unfortunately so I'm looking for other ideas...
Any clue?
Thanks a lot!
The thing is the code is huge and I can't copy/paste everything here.
Here's more info:
- in the page I have a template that's used by the javascript to populate a div with all the contents. I have ~20 results.
The template starts like this :
<script id="itemtemplate" type="geowacht/template">
<div class="results-name">
<h4 class="show-more-name itemaponaam" data-target="#result"></h4>
<div class="d-block" id="result">
<div class="itemaddress"></div>
<div class="itemgeodesc"></div>
<div class="itemphone"></div>
The pages calls a script that queries an API and returns 20 results. These results are parsed and added to the page using the template.
Here's the beginning of the code:
apiconfig.default_populateItemNode = function(node, apotheekData, wachtPeriodeData, authenticationData) {
$(node).find('.itemaponaam')[0].setAttribute('data-target', '#result-' + itemPos);
$(node).find('.buttons')[0].setAttribute('id', 'result-' + itemPos + '-buttons');
$(node).find('.buttons')[0].setAttribute('data-apbnb', apotheekData.pharmacy.id);

How do we set mask on a dynamically inserted input?

Since Angular-UI-Mask is acting oddly, I'm using jquery-inputmask to some of my inputs, but when an input is dynamically inserted ny Angular it gets no mask:
<li ng-repeat="item in items">
<input type="text" name="birth_date" class="span2 format_date" ng-model="birth_date" placeholder="Data de Nascimento" required />
</li>
This is the related script
<script type="text/javascript">
jQuery(document).ready(function(){
$(".format_date").inputmask("99/99/9999");
});
</script>
Is there anything I can do to force it to set the mask to new inputs?
jQuery plugins like jQuery.inputMask work by (as your code shows) attaching behaviour to DOM elements when the document is 'ready'. This will run once, and never again, so for dynamically-added content this approach doesn't work.
Instead, you need something that will run whenever the corresponding DOM is changed. So whenever an 'item' in your 'items' list is added, the element is added and the corresponding jQuery function is run against that element. You need to use AngularJS for this and you could write your own directive, but thankfully, someone has already written the code for you: the jQuery Passthrough plugin as part of Angular UI's UI.Utils.
Here is a working Plunkr.
You need to include the script at the top, like so (I downloaded it from GitHub):
<script src="ui-utils.jq.js"></script>
Load the module into AngularJS, for example:
var app = angular.module('myApp', ['ui.jq']);
And then use the directive in your HTML markup:
<input type="text" ui-jq="inputmask" ui-options="'99/99/9999', { 'placeholder': 'dd/mm/yyyy' }" />

jQuery on method with ajax response [duplicate]

This question already has answers here:
Event handler not working on dynamic content [duplicate]
(2 answers)
Events triggered by dynamically generated element are not captured by event handler
(5 answers)
Closed 9 years ago.
I have a <select class="listenToMe" /> that when changes does something. I also have a separate link that when clicked performs an ajax request and returns more dom elements and inside them it has another <select class="listenToMe" />
I would like my event listener to be applied to this element as well. I am trying to use jQuery's on method but it doesn't appear to be working:
JS
var selectListener = function() { alert('you change me!'};
$('.listenToMe').on("change", selectListener);
$('.addMore').click( function() {
$.post('myWebPage.php', {} , (function(data) {
$(this).before(data);
// data is something like <div><select class="listenToMe" /></div>
}).bind(this));
});
HTML
<div>
<div>
<select class="listenToMe" />
</div>
<div>
<select class="listenToMe" />
</div>
<a class="addMore">Click me</a>
</div>
Any suggestions?
You can try
$(document).on('change', '.listenToMe', function(){
// Your code here
})
Your using on like live. The difference is subtle but important. You attach on to a static element in your markup and then filter the event based on a selector. This way the on event never goes out of scope, e.g. if I have the markup
<!-- this div is not dynamically loaded-->
<div id="mystaticDiv">
<!--insert dynamic content here-->
</div>
which when I add my dynamic markup will become:
<!-- this div is not dynamically loaded-->
<div id="mystaticDiv">
<!--insert dynamic content here-->
<div class="myDynamicdiv></div>
</div>
To fire an event on the click of my dynamic div that never needs rebinding I would write the following jQuery:
$('#mystaticDiv').on('click', '.myDynamicdiv', function() {/*Do stuff*/});
So I'm binding my on to the #mystaticDiv but filtering on .myDynamicdiv. Now I can add as many .myDynamicdivs as I want and this will work.
I mentioned live. This is deprecated but works in the same way as you were attempting. This attaches an event to the document of the page with a selector base on the selector your attaching live to. So $('.myDynamicdiv').live('click', function() {/*Do stuff*/}); is directly equivalent to $(document).on('click', '.myDynamicdiv', function() {/*Do stuff*/});. the reason I mention this is this is how you were trying to use on.
Your code $('.listenToMe').on("change", selectListener);. Will not work for dynamic content. This code attaches the event to the dynamic content, that doesn't exist yet. So the is never actually bound. Interestingly $('.listenToMe').on("change", selectListener); is exactly what $('.listenToMe').change(selectListener); does under the hood.

Get closest parent with stated attribute?

I have the following code :
<input pid="hidVoteKey" type="hidden" value="0" />
<ul id="mainPostList" class="verticalList">
#foreach (var postViewModel in Model.Posts)
{
<li><div class="voteCon">...</div></li>
}
</ul>
Then I have a jquery that loop all elements with class voteCon and then try to get the parent input like this :
$(".voteCon").each(function () {
InitVoteControl($(this), $(this).parent("input[pid='hidVoteKey']").val());
});
The problem is that it will not find the hiddenfield?
In this case the voteCon contains up/down buttons and there is some javascript functions bound here to make ajax calls. There will be multiple lists like the one above on the same page but thay all will have diffrent hidVoteKey.
It won't find the <input> because it isn't a parent (or ancestor) of the <div class="voteCon">. It's on the same level as (a sibling of) the <ul>, which is an ancestor of the <div>. You could do this:
$(this).closest('ul').prev('input[pid="hidVoteKey"]').val()
Just use then not type hidden , you can use inline style="display:none;"

Categories