Dynamically added element is forcing a reload - javascript

I have a table that has a range of elements inside of it.
I have registered some event handlers and add these elements dynamically through injecting new html.
Everything works for the original elements, but when I add a new ( dynamically created element ) and trigger one of the newly created event handlers the page seems to force reload and I lose all of my data/content.
Here is a simplified version:
<table id="asset-table" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr class="asset-row">
<td align="center" style="padding-bottom: 32px;">
<form id="asset-title" class="asset-title">
<input type="text" placeholder="Add Asset Title Here ..." id="title-input1" />
<input id="title-submit" class="submit" type="submit">
</form>
<p id="asset-title-replacement" style="text-align: center; display: none;"><strong></strong></p>
</td>
</tr>
</table>
<button id="add-asset-button">Add Asset</button>
EventHandler
// asset title load
$('#asset-table').on("submit", '.asset-title', function (event) {
event.preventDefault();
console.log("asset-title");
let text = $(this).find('#title-input1').val();
let td = $(this).children().first()
td.find('.asset-title').hide();
td.find('.asset-title').next('p').text(function () {
return text
}).css("font-weight", "Bold");
td.find('.asset-title').next('p').show()
});
To add the new element:
$('#add-asset-button').click(function () {
console.log("adding asset");
$('#asset-table').append("<tr class='asset-row'><td align='center' style='padding-bottom: 32px;'><form id='asset-title' class='asset-title'><input type='text' placeholder='Add Asset Title Here ...' id='title-input1' /><input id='title-submit' class='submit' type='submit'></form><p id='asset-title-replacement' style='text-align: center; display: none;'><strong></strong></p>/td></tr>")
})
So when dynamically adding the element, everything goes as planned, it is once I interact with adding the new "title" and click submit that the page seems to reload and I lose any "state" and content previously handled.
The problem that I am having is that the eventHandler is bound to #asset-table, so when a dynamic element is created the "this" keyword is bound to the #asset-table element, so how can I specify which that I am working with for that event??

You will have multiple forms with the same id as you have it now, though that's not the main problem. You're also listening to .assetTitle rather than .asset-title and your listener is wrong, which are the main problem. When you do:
$('.asset-row').on("submit", '.assetTitle', function (event) {
...
})
jQuery will only listen for form submits within .asset-row that currently exist when the listener is attached. All of your subsequent .asset-rows with .asset-titles (not .assetTitle) that are created programmatically do not have listeners attached. Instead, you need to apply it to an element that is present when the listener is attached, and then listen to any programmatically-created elements within it, like this. Note that I've changed .assetTitle to .asset-title.
$('#asset-table').on("submit", '.asset-title', function (event) {
...
})
Fiddle

Related

AddEventListener to svg file and not button that makes it clickable

Here is my objective :
I want to have a button on my html page that when it is clicked, makes and svg clickable, and when that svg is clicked, the page displays the message "clicked".
I have an html file where I create a button and a div that holds an svg file :
HTML File
*....*
<div>
<p>This will be replaced <br/>
<span id="id_to_be_replaced" style="color: red; font-weight: bold;"> Replaced normally </span>.</p>
</div>
*....*
<br />
<input name="button5" type="button" id="myButton5" onClick="button5_makeClickable('svgfile');" value="4. Svg clickable" />
<br />
*....*
<div>
<object id="svgfile" data = "exemple.svg" type="image/svg+xml"> </object>
</div>
What I want is that when I press my button5 it is supposed to make the svg file clickable, the function that I call are in another javascript file :
JS File
*....*
function setName(name) {
var elementHtmltoFill = window.document.getElementById("id_to_be_replaced");
elementHtmltoFill.innerHTML = name;
}
*....*
function button5_makeClickable(id) {
var drawing = document.getElementById('svgfile');
drawing.addEventListener('click', setName("clicked"));
}
*....*
However what happens is that "clicked" is displayed as soon as I click on the button (and not the svg).
I don't understand why since I add the event listener to drawing (therefore the svg)?
Thank you for your help !
PS : I am trying to understand the use of the function addEventListener, so I would prefer if your help uses it please.
You must make sure you listener for the svgfile object is outside the onclick function for your button otherwise you can only ever click on the svgfile object whenever you are simultaneously clicking on the button.
By doing setName("clicked") you instantly call the setName function instead of passing a reference to the function. You can solve this passing a function as the event handler argument in which you call setName("clicked").
function button5_makeClickable(id) {
var drawing = document.getElementById('svgfile');
drawing.addEventListener('click', function() {
setName("clicked")
});
}
But, your code currently is constantly adding event listeners to the <object> element. The element only needs 1 click event listener.
Use a flag. This a boolean (true / false) which acts like a switch to do or to not do something based on its value. Add a event listener to the <object> element and check in the event handler if the flag is true. If it is, change the text inside your span, if it isn't, don't do anything.
The example below shows how this could work.
Also, prevent using inline event listeners in your HTML. Using addEventListener is the way you should be learning how to attach event handlers.
var myButton5 = document.getElementById('myButton5');
var drawing = document.getElementById('svgfile');
var svgIsClickable = false;
function setName(name) {
var elementHtmltoFill = window.document.getElementById("id_to_be_replaced");
console.log(elementHtmltoFill)
elementHtmltoFill.innerHTML = name;
}
function button5_makeClickable(event) {
svgIsClickable = true;
}
myButton5.addEventListener('click', button5_makeClickable);
drawing.addEventListener('click', function() {
if (svgIsClickable === true) {
setName("clicked");
}
});
object {
display: block;
width: 50px;
height: 50px;
background: gray;
}
<div>
<p>This will be replaced <br/>
<span id="id_to_be_replaced" style="color: red; font-weight: bold;"> Replaced normally </span>.</p>
</div>
<br />
<input name="button5" type="button" id="myButton5" onClick="button5_makeClickable" value="4. Svg clickable" />
<br />
<div>
<object id="svgfile" data="exemple.svg" type="image/svg+xml"> </object>
</div>

Jquery not detect click after append of element

i've made a sortable list with html5sortable library, where user have a category, where can add products inside, and can add other categories where to add products too.
The first one category is already displayed, and work fine when adding products, but if I add another category I can't add product on this category.
I've tried with "on" method instead of "click", as it should work on element created dinamically, but I think I'm using it in the wrong way.
Here is the HTML
<div class="container-categorie">
//THIS IS A CATEGORY ITEM, WITH ADD PRODUCT BUTTON INSIDE
<div class="category" id="categoria-1">
<input type="text" class="input-categoria" placeholder="NAME CATEGORY" autofocus="autofocus">
<div class="list" id="list-1">
//products will be added here
</div>
<div class="item-products add-product-container">
<div align="center" class="add-product-btn" id="addproduct-1">
+ add product
</div>
</div>
</div>
//END OF CATEGORY ITEM
<div class="add-categoria-container">
Nuova categoria
</div>
</div>
And here is the js code:
var i = 2;
$(".categoria").on('click', function() {
//to delegate the click I applied this event to "add product" button's container
});
//This add a product inside category div
$(".add-product-btn").click(function() {
var id_btn = $(this).attr("id");
var single_id = id_btn.substring(id_btn.indexOf("-") + 1);
$("#list-"+single_id).append('<div class="item-products">prova</div>');
sortable('.list');
});
//This should add a new category inside "container-categorie", with an "add product"
//button inside, to add products inside this new category
$(".add-categoria-container").click(function() {
$(".sortable-categorie").append('<div class="categoria"><input type="text" class="input-categoria" placeholder="NOME CATEGORIA PRODOTTI" autofocus="autofocus"><div class="list" id="list-'+i+'"></div><div class="item-products add-product-container"><div align="center" class="add-product-btn" id="addproduct-'+i+'">+ Aggiungi prodotto</div></div></div>');
sortable('.sortable-categorie');
});
Jquery can't listen to dynamically generated Elements directly, what you can do is you can provide parent element which is already there in DOM listen to them.
$("element which is already in DOM").on('click',"element to listen to", function() {
//Code here
});
$(".container-categorie").on('click',".categoria", function() {
//Code here
});
or you can directly listen to the body instead, but it is not preferable.
For Ref : https://api.jquery.com/on/#on-events-selector-data-handler
Try this
$(document).on('click',".your_class_name", function() {
//to delegate the click I applied this event to "add product" button's container
});
JQuery has no virtual DOM like Angular or React. That means JQ can't "see" the dynamically generated element. What you can do is using a onclick="functionYouWantToTrigger(someParameters)" directly in the HTML tag.

Can't get access to the clicked element

I have input search and table, that generates automatically if I change input.
<div class="search">
<form action="" method="post" class="searchform" >
<input type="search" name="" placeholder="Search" class="inputsearchform" ng-model="search"/>
<input type="submit" name="" value="" class="submitsearchform" />
</form>
</div>
<div class="songlist">
<table id="songlistTableR" ng-app='test_table' ng-controller='main_control'>
<tr><th>Name</th><th>Link</th></tr>
<tr ng-repeat="data in loaded | filter:search">
<td><i class="fa fa-plus"></i>{{data.song_name}}</td>
<td><a href="{{data.link}}" target='_blank'>Youtube</a></td>
</tr>
</table>
</div>
JS script for generating data in table is:
var app = angular.module('test_table', []);
var n = [];
app.controller('main_control',function($scope,$http, $rootScope){
load();
function load(){
$http.get("http://localhost:7001/load").success(function(data){
$rootScope.loaded=data;
n = data;
});
}
});
I also have code, that shows table when input.value != 0
$('.inputsearchform').bind('input', function() {
if($(this).val() == 0){
songlistTableR.style.visibility = 'hidden';
}
else{
songlistTableR.style.visibility = 'visible';
}
});
Everything works good, but I can't get access to element .fa.fa-plus when it's clicked. The only two ways, that work are putting $(".fa.fa-plus").click(function(){}); in bind event or creating function outside window.onload that will work if I put in HTML file onclick for .fa.fa-plus element.
First method is bad, because if I change input more than one time, click function will work every time, even if I didn't click on this element.
Second method also isn't appropriate because I need to know index of clicked element.
Can anybody advise me a solution?
UPD Sorry for interruption, I solved this problem. I changed the version of Jquery to 2.2.2 and now it's working. Thanks for helping!
You may want to try to attach the event handler to the document but filtering on your selector. In this way even if the inner DOM changes, your handler will always intercept the event triggered by elements having the class fa fa-plus
$(document).on("click",".fa.fa-plus", function(){});
As #mhodges pointed out, this is a delegated event.
try to change this:
<td><i class="fa fa-plus"></i>{{data.song_name}}</td>
with:
<td><a ng-click="doSomething($event)" ><i class="fa fa-plus"></i>{{data.song_name}}</a></td>
then in the controller:
$scoope.doSomething() = function(event){
do what you need
}
just handle events in the angular way

Jquery will not remove div

I have form in which I am adding some dynamic actions. I have a table in which I have rows of positions
applicants applied for. There is an offer postion button and when they click on the offer button I want to insert the offer fields to be submitted and updated. I can get the feilds to insert but when I click on the cancel transaction button, I can't get it to empty the div addapptrans where the form is built. Below is the code. I know it must be something simple I am missing.
<head>
<script type="text/javascript">
$(function(){
$(".offerposition").click(function(){
var row = $(this).closest('tr').find('td:nth-child(1)').text();
alert('You clicked on ' +row);
$("#addapptrans").empty();
$("#addapptrans").append(
$("<input>").attr('type','hidden').attr( 'value',row).attr('Name','Mchposid'))
.append(
$("<input>").attr('type','submit').attr( 'value','Complete Offer').attr('id','completeoffertrx').attr('name','completeoffertrx').addClass("buttonlarge buttonmargin")
).append(
$("<input>").attr('type','button').attr( 'value','Cancel Transaction').attr('id','canceloffertrx').attr('name','canceloffertrx').addClass("buttonlarge buttonmargin")
);
}
)
}
);
$(function(){
$("#canceloffertrx").click(function(){
$("#addapptrans").empty();
})
})
</script>
</head>
<body>
<form >
<div id="addapptrans"></div>
<p class="posttitle">Positions applied For</p>
<table class="tabpositions">
<tbody>
<tr>
<th class="position">Position</th>
<th class="department">Department</th>
<th class="dateapp">Date Applied</th>
<th class="appdate">Offer?</th>
</tr>
<tr>
<td style="display: none;">2281</td>
<td>Building Service Worker - Part time</td>
<td>Environmental Services</td>
<td>08/13/2001</td>
<td><input type="button" class="offerposition" value="Offer Position"></td>
</tr>
</tbody>
</table>
</form>
This code here:
$(function(){
$("#canceloffertrx").click(function(){
$("#addapptrans").empty();
})
})
Runs before #canceloffertrx exists on the page. So $("#canceloffertrx").click(fn) Matches zero elements on the page, and binds a click handler to all zero of them.
You can fix this by binding the click handler to the document, or closest parent that is present, instead.
$('#addapptrans').on('click', '#canceloffertrx', function(){
This says that when the element #addapptrans receives a click event, and element that matches the selector #canceloffertrx was the one that was actually clicked, fire the event handler function.
Or by binding the click handler when you create the button.
$("<input>")
.attr('type','submit')
.attr( 'value','Complete Offer')
.attr('id','completeoffertrx')
.attr('name','completeoffertrx')
.addClass("buttonlarge buttonmargin")
.click(function() { ... });
Lastly, some style advice :) Especially when chaining jQuery methods, you can put each call on it's own line which makes it much more readable.
And you should also know that attr() can accept an object as an argument, allowing to call it just once to set many attributes.
$("<input>")
.attr({
type: 'submit',
value: 'Complete Offer',
id: 'completeoffertrx',
name: 'completeoffertrx'
})
.addClass("buttonlarge buttonmargin")
.click(function() { ... });

jQuery cannot recognize appended cloned elements using jQuery.appendTo()

I want to make a list of group inputs allow user to dynamically let user add/remove group row:
<div id="div-form-denominations" class="form-denominations">
<div id="row-0" class="form-denomination">
<div class="form-field">
<div class="form-field">
<div class="form-field">
<input id="_denominations[0].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[0].id.reference" value="87-070329-034COP-4444">
<input id="_denominations[0].denomination" class="removableHiddenDenom" type="hidden" name="denominations[0].denomination" value="10000">
<a id="deleteBtn-[0]" class="action-link delete-denomination" href="#">
<div class="spacer"></div>
</div>
<div id="row-1" class="form-denomination">
<div class="form-field">
<div class="form-field">
<div class="form-field">
<input id="_denominations[1].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[1].id.reference" value="87-070329-034COP-4444">
<input id="_denominations[1].denomination" class="removableHiddenDenom" type="hidden" name="denominations[1].denomination" value="">
<a id="deleteBtn-[1]" class="action-link delete-denomination" href="#">
<div class="spacer"></div>
</div>
<div id="row-2" class="form-denomination">
<div class="form-field">
<div class="form-field">
<div class="form-field">
<input id="_denominations[2].id.reference" class="removableHiddenOrder" type="hidden" name="denominations[2].id.reference" value="">
<input id="_denominations[2].denomination" class="removableHiddenDenom" type="hidden" name="denominations[2].denomination" value="">
<a id="deleteBtn-[2]" class="action-link delete-denomination" href="#">
<div class="spacer"></div>
</div>
<div id="row-3" class="form-denomination">
.......
</div>
So each row include a group of form-field which include an input or select component(not show in above code) and some hidden fields and a delete link to remove current row from view.
Also I create a link to dynamic add a new row into the section
var rowTemplate = null;
j(document).ready(function() {
// Save the row template
rowTemplate = j('.form-denomination:first-child').clone();
j("#add_link").click(function() {
add_denomination();
});
});
and here is the content of add_denomination function that clone the first row and replace any cloned id with new index, and append the cloned row after last row of the list.
function add_denomination() {
var index = j('.form-denomination').length;
// set the new row id
var newRowId = 'row-' + index;
var newRow = rowTemplate.clone().attr('id', newRowId);
// Replace the id/name attribute of each input control
newRow.find('div, input, select, a').each(function() {
replaceAttribute(j(this), 'id', index);
replaceAttribute(j(this), 'name', index);
j(this).val('');
});
// add new element to the DOM
newRow.appendTo('.form-denominations');
alert("new list size = " + j(".delete-denomination").length);
console.log("DONE!");
}
each time click on the add-link the pop up alert show the new list size (j(".delete-denomination").length increment by 1), which in my understanding, new row appended successfully.
The problem is the following method
// delete denomination row
j('.delete-denomination').click(function () {
j(this).parent().remove();
}
ONLY WORKS FOR THE NON-CLONED ROW !!! Using firebug I can clearly see the appended row is successfully appended with same structure and same element as original rows but only difference is the id.
However, each time when I click on deleteBtn-[i], in which i is the cloned/appended row's index, the code even not going into the j('.delete-denomination').click() function, which in my understanding, Dom or jquery didn't recognize the new rows hence the failure of identifying the link by jQuery. It's kind of contradictory to the previous alert message that telling the size of list has grown.
But when I click on deleteBtn-[i] where i is the non-cloned row, everything works fine...
So the question is: how to append/add new doms and make them identified by jQuery or Dom? What is wrong in above processing? Is there any way to refresh the list so that Dom/jQuery understand the appended rows from all perspective?
jQuery 1.7+
j(".form-denomination").on("click", ".delete-denomination", function(){
j(this).parent().remove();
});
jQuery 1.3+
j(".delete-denomination").live("click", function(){
j(this).parent().remove();
});
jQuery 1.4.3+
j(".form-denomination").delegate(".delete-denomination", "click", function(){
j(this).parent().remove();
});
The problem is a matter of order and when expressions are evaluated. When you call jQuery with a selector, the selector is evaluated at that time to select the matching elements which exist at that time. The click handler is then registered to only those elements. Elements which are created later are, naturally, not affected.
One solution, demonstrated in another example, uses jQuery's "live events" to apply the selector at the time each event is fired to determine what elements, if any, it would match. There is a performance implication to this approach.
Another solution is to register the desired event handler on the newly created elements when you create them.
Add 'true' to the clone method in order to copy the data as well as the events attached to the original element.
rowTemplate = j('.form-denomination:first-child').clone(true);
This is disabled by default. Here is the clone documentation:
https://api.jquery.com/clone/
P.s. You don't need the click function within the document.ready and it won't bind until after the click.

Categories

Resources