Dojo Event dynamically created events not handled - javascript

I have the following Dojo Javascript snippet:
postCreate: function () {
this.inherited(arguments);
var hitchWidgetToaddLevelButtonClickHandler = DojoBaseLang.hitch(this, addLevelButtonClickHandler);
DojoOn(this.AddLevelButton, "click", hitchWidgetToaddLevelButtonClickHandler);
function addLevelButtonClickHandler() {
hierarchyLevelNumber += 1;
var myButton = new Button({
label: "Click me!",
id: "level_button_" + hierarchyLevelNumber,
onClick: function(){
// Do something:
console.log("Hello there, ");
}
}).placeAt(someNode,"last").startup();
var registeredRemovalButton = DijitRegistry.byId(("level_button_" + hierarchyLevelNumber));
DojoOn(registeredRemovalButton, "click", function someFunction() {
console.log("how are you?");
});
}
}
and the following HTML
<div id="settingsBorderContainer" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:false, region:'top'" class="${theme}">
<table data-dojo-attach-point="HierarchyLevelsTable">
<tr>
<th>Level</th>
<th> Table Name </th>
<th> Column Name</th>
</tr>
<td id="someNode">
</td>
</table>
</div>
The goal here is, when AddLevelButton is clicked it dynamically creates a row in the table with the necessary information and a new button for each row. Note that the HTML and Javascript are largely simplified to illustrate my problem. I did not include all of the row logic that occurs in addLevelButtonClickHandler() method. The HTMl is built as intended, as you can see in the following screenshot:
The problem is that everytime I add a button, only the last button's event listeners work, i.e. in the case of the screenshot the button circled in red logs: Hello there, you called the remove handler [object MouseEvent], and the previous button in the table no longer can be clicked and catch the necessary event. Sorry if the code is a gross-simplification of my goal, I appreciate any help that is offered.
*********EDIT**************
So I took a different approach and placed only 1 button total, and delete level by level from the bottom up. As long as it resides outside of a <table> it functions..Which brings me to the conclusion of NEVER PUT DIJITS OR BUTTONS IN A TABLE After many hours lost, I would recommend if you absolutely need a table in the first place, just placing the button next to the table as necessary using CSS and floats
I will leave this question as unanswered until someone may be able to offer an explanation of why the inner table approach does not work.

While working with dojo. You should never use node.innerHTML to add or update nodes. It will basically convert all the dijits into HTML string and parse as regular HTML by the browser. which in turn will destroy dom object and event associated to it.
Instead create dom object using dojo/dom-construct and add it with appendChild for DOM nodes or addChild in case of container dijits.

Related

jQuery .bind on change some object with .class

i interested in learn how to correctly add .bind code to object, then he changed. This object is not unic and have class selector insted of id, but it have a <div> wrapper:
<div id="GDI">
<table class = "Tiberium">
...
<tbody>
<tr>...</tr>
...
<tr>...</tr>
</tbody>
</table>
</div>
<div id="NOD">
<table class = "Tiberium">
...
<tbody>
<tr>...</tr>
...
<tr>...</tr>
</tbody>
</table>
</div>
The data changed in table with class "Tiberium" in <tbody> space (e.g. was added new row), i need simple alert then data changed in GDI table, but dont know how to do it.
Code that i tried:
$('.Tiberium').bind('DOMSubtreeModified', Alert);
Where Alert is function.
This code capture changes in both tables, and i got alerts then one of them changed. So how i can track changes only in "Tiberium" table in GDI space?
p.s. i'v tried $('#NOD').bind('DOMSubtreeModified', Alert);
but this code alert me 3 times in row, and it possible run every code in function 3 times. (i think it happend in case of this hierarchy).
The DOMSubTreeModified event is deprecated. A better alternative to this solution is to use the MutationObserver.
var toBeObserved = document.getElementsByClassName('Tiberium');
if('MutationObserver' in window) { // ensure browser support
var observer = new MutationObserver(myEventHandler); // instantiate
observer.observe(toBeObserved, { // start observing
childList : true,
subtree : true
});
}
Everytime the toBeObserved element is mutated, the myEventHandler function will be called. You can add your custom code within this function.
Could you please try following code:
$('.Tiberium').bind('DOMSubtreeModified', function(){
//check the parent div's id
if($(this).parent().attr("id") == "GDI")
Alert //your alert function call
});

Duplicate div with script/style tags inside using JS

I have the following html code:
<table><tbody><tr><td>
<div id="div_1">
<style>...</style>
<div><label> 1 </label></div>
<div><input type="text" name="text_1"/></div>
<script>$("#text_1").mask("99/99/9999");</script>
<div><label><a onclick="javascript:insert_div()"> </a></label></div>
...
</div>
...
<div id="div_20">
<style>...</style>
<div><label> 1 </label></div>
<div><input type="text" name="text_20"/></div>
<script>$("#text_20").mask("99/99/9999");</script>
<div><label><a onclick="javascript:insert_div()"> </a></label></div>
...
</div>
</td></tr></tbody></table>
That generates this (from 1 to 20, actually):
What I need is to insert a whole new div when the user presses the arrow button. It should copy the div with scripts and styles and insert after them with a new number (e.g. 21, then 22, etc.).
This is purely an instructional example of an alternate way of doing this task. It is intentionally wordy to provide ideas.
Suggestion: Avoid attribute-based event handlers when using jQuery:
To clarify my first comment. If you use onclick=javascript handlers, you are placing the registration of the event in the HTML, separate to the actual handler in the script. The "jQuery way" is to apply the handler function to a selection of elements, using methods like .click() and the rather useful .on() which I use below. This makes maintaining pages easier as you are not hunting through the HTML for JavaScript snippets. jQuery event handlers also support having more than one handler, for the same event, attached to an element which you simply cannot do with onclick=.
Concepts shown:
Use a global counter for the next id number and simply increment it after each use
Use a delegated event handler to process the "add" clicks as the elements are added dynamically (so do not exist until later).
Use a template stored in a dummy <script> block to hold your template HTML (this text/template type is unknown so is ignored by all browsers. It also makes maintenance a breeze.
Replace placeholder markers in the template with the new id information
Convert the HTML to DOM elements using $(html)
Find descendants in the new row to add things like mask.
Append the new row
JSFiddle: http://jsfiddle.net/TrueBlueAussie/Lu0q0na2/2/
// Declare a global counter for our new IDs
id = 2;
// Listen for click events at a non-changing ancestor element (delegated event handler)
$(document).on('click', '.addnew', function(e){
// get the HTML of the template from the dummy script block
var template = $('#template').html();
// Change the template names etc based on the new id
template = template.replace('{name}', 'name' + id).replace('{id}', id);
// Increase next id to use
id++;
// Convert the HTML into a DOM tree (so we can search it easily)
var $template= $(template);
// Apply the mask to the newly added input - alter this to suit
$template.find('input').mask("99/99/9999");
// Append the new row
$('table').append($template);
// stop the link from moving to page top
return false;
});
I will be happy to explain any part of this if you have questions. I realise it may be a bit of a shock compared to the existing way of doing it you have :)
I give you the basic idea: the rest if left as an exercise as teachers say:
<script type="text/javascript">
var last_inserted = 0;
function insert_div(){
$d = $("#div_" + last_inserted).clone();
$d.attr('id', 'div_' + last_inserted++);
$("table").append($d);
}
</script>
And something else: <a onclick="javascript:insert_div()"> is probably not correct (untested).
Either: <a onclick="insert_div()"> or <a href="javascript:insert_div()">

Rendering dynamic Enyo Repeaters

I need to make a data table for an enyo project I am working on that will ultimately display the result of an Ajax call.
This (Blatantly stolen from ryanjduffy here)seems to be a good starting point, but when I try to call setData() from a button event (rather than in the constructor) as I have here I get the following error:
InvalidCharacterError: String contains an invalid character # http://enyojs.com/enyo-2.1/enyo/source/dom/Control.js:681
I looked at the Control.js code and it seems that it tries to create a new node, but the this.tag property is set to null and things break.
I feel like I am missing something really simple, but I just can't see the problem yet...
Can someone tell me what I am doing wrong?
Thanks!
EDIT 1:
Apparently calling render() is not needed. Here is the original working version with render() commented out. Everything looks great. However, if I try to remove render() from the version that requires a button click, the repeater starts creating div's above the table instead of tr td's inside the table...
EDIT 2:
Basically, from what I can tell, the Repeater inside of a table will lose it's parent once the table is rendered (or something like that). The result is the Repeater will start rendering its new items outside of the original table and because a td tag without a table makes no sense, it just renders a div.
The solution I have come up with is to give the Repeater itself a table tag so its children always wind up in the right spot. This adds the challenge of needing to recreate the header line each time, but it is not that big of a deal. I have a working example if anyone is interested.
I'm sure you're not looking for a solution any longer but since I was mentioned in the post, I thought I'd let you know what I figured out. In short, my code shouldn't have worked but since browsers are forgiving, it did ... sort of.
When the code run at create time, it renders something like this:
<table>
<tr> <!-- header row --> </tr>
<div> <!-- repeater tag -->
<tr> <!-- repeater row --> </tr>
</div>
</table>
The browser looks at that and says, "Hey, dummy! No <div>s in a <table>" and kicks it out but leaves the <tr>s.
In your example, since you're delaying the render of the rows, Enyo renders:
<table>
<tr> <!-- header row --> </tr>
<div></div>
</table>
And the browser ejects the <div> and you're left with an empty table. When you later set the data, those rows are rendered into the div. Unfortunately, since you're rendering <tr> and <td>, those aren't valid outside a table so you just get text.
I found a couple solutions. The simplest was to set the tag of the Repeater to be TBODY which is allowed inside a table. The slightly more involved solution was to make the DataTable inherit from Repeater and set the header row to be chrome so they're not removed when updating the data.
Option #2 Fiddle
enyo.kind({
name:"DataTable",
tag: "table",
kind: "Repeater",
published:{
map:0,
data:0
},
handlers: {
onSetupItem: "setupItem"
},
components:[
{name:"row", kind:"DataRow"}
],
create:function() {
this.inherited(arguments);
this.mapChanged = this.dataChanged = enyo.bind(this, "refresh");
this.refresh();
},
refresh:function() {
if(this.map && this.data) {
this.buildHeader();
this.setCount(this.data.length);
}
},
buildHeader:function() {
if(this.$.header) {
this.$.header.destroyClientControls();
} else {
this.createComponent({name:"header", tag:"tr", isChrome: true});
}
for(var i=0;i<this.map.length;i++) {
this.$.header.createComponent({content:this.map[i].header, tag:"th"});
}
this.$.header.render();
},
setupItem:function(source, event) {
for(var i=0;i<this.map.length;i++) {
event.item.$.row.createComponent({content:this.data[event.index][this.map[i].field]});
}
event.item.render();
return true;
}
});

independently working div in Jquery

I am trying to make an independently working div which has a form inside of it.
I use jquery to calculate the price of a product depending of the user's selections in the form. However the user is able to add multiple items in his 'cart' so the form is duplicated to another div. The problem is that the calculation pattern can't separate these two divs and the calculation will be incorrect. The form is also interactive so it will be generated by the user's input. This is really complex set and renaming every variable by the 'product number' doesn't sound really efficient to me.
I'm kind of stuck here and i don't really know how to solve this problem. I had an idea that what if I put an iframe inside of the div and load my form and its calculation script inside of it, and then use post command to transfer the price of the product to the 'main page' to calculate the total price of all of the products the user wanted.
However it seems that jQuery scripts doesn't work independently inside of these iframes, they still have connection so they broke each other.
i will appreciate any kind of suggestions and help to solve this matter, thank you!
here's the code so far
Heres the body
var productNumber = 1;
<div id="div_structure">
</div>
<button id="newProduct" >Add new product</button><br \>
add new item
<!-- language: lang-javascript -->
$('#newProduct').click(function ()
{
$('<div id="productNo'+productNumber+'">')
.appendTo('#div_structure')
.html('<label onclick="$(\'#div_productNo'+productNumber+'\').slideToggle()">Product '+productNumber +' </label>'+
'<button onclick="$(\'#product'+productNumber+'\').remove()">Remove</button>');
$('<div id="div_product'+productNumber+'" style="display: none;">').appendTo('#product'+productNumber+'');
$('<iframe src="productform.html" seamless frameborder="0" crolling="no" height="600" width="1000">').appendTo('#div_product'+productNumber+'');
productNumber++;
});
it also has a function that allows the user to remove the inserted div.
Here's just few lines from the productform
$(document).ready(function()
{
$('#productCalculation').change(function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
<form id="productCalculation">
<div id="div_productShape" class="product1">
<h1>Select the shape of the product</h1>
<input type="radio" name="productShape" value="r1">R1</input><br \>
<input type="radio" name="productShape" value="r2">R2</input><br \>
<input type="radio" name="productShape" value="r3">R3</input><br \>
</div>
.
.
.
</form>
I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable
There are two problems with this code: You say somewhere "I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable". This is because event handlers are attached to elements that are in the DOM at that specific moment. To get it to work for all elements, use event delegation:
$(document).ready(function()
{
$(document).on( 'change', '#productCalculation', function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
Your other question is "My question in a nutshell: Is there a way to restrict jquery to function only in certain div even though i use the same variable names in the second div ". You can use the this variable to access the element the click was invoked on. From this element you can traverse the DOM if needed, for example with .parent().
$('div').on( 'change', function( e ) {
console.log( $(this).val() );
} );

.focus() doesn't work on an input while orher attributes works

I have a classic table / thead / tbody structure, which I add a line at the end of the tbody. The line contains only an input element. The code works in Firefox 3.6 but not in Chrome v5 or IE8. I'm using jQuery 1.4.2.
Does not work:
$("#" + AJAX_ID).parent().find('tr:last > td:nth-child(2) > input').focus();
Does work:
$("#" + AJAX_ID).parent().find('tr:last > td:nth-child(2) > input').css('background-color', 'red');
even setting an ID on the input, and using document.getElementBuId('id').focus() doesn't work.
*** edit ***
The site is pretty complex (mix of template / static html and dynamic html), but the table
looks like this (rendered html in chrome, layout via tidy) : http://pastebin.com/PHqxAEVm
*** edit 2 ***
Even $("#lectures").find("input:last").focus(); called from the ajax callback doesn't do anything.. $("#lectures").find("input:last").css('background-color', 'red'); turns one red though, but the focus goes to the address bar.
Here's a dump of the returned object from the selector:
http://pastebin.com/Jdw1TZXf
*** edit 3 ***
Here's the JavaScript code that builds the table: http://pastebin.com/cbCfi0UY
on page load, oContainer is $("#lectures") while after the ajax call it's $("#" + AJAX_ID).parent(), which is supposed to point to the table's tbody
*** edit 4 ***
Hard problem... here's the full lectures.js file: http://pastebin.com/Jkg0DZqa
batisses and compteurs are json objects loaded via the template. the user select a batisse then a compteur then press a button that calls buildAjout(), which calls in this example buildElectric($("#lectures"), compteur);. Once the line is filled bu the user, onBlurLecture(tr_parent) is called, the data is sent to the server via AJAX and function callback_compteurs_lecture_add(AJAX_ID) is called after the ajax call is complete. The function SendCommand is a custom function which use jQuery ajax.
The creation of the first input line (and the focus) works, but not the one created in the callback of the ajax.
*** edit 5 ***
The full rendered page looks like: http://pastebin.com/UfBYcjX3
I shortened the batisses variable. The (full) page has no JavaScript errors.
In Chrome's JavaScript console, I cannot focus the inputs.
*** edit 6 ***
Wrong function name in this question for SendCommand. fixed.
Solution found:
.focus() doesn't work on an input while orher attributes works
What ID are you targeting? Because if I replace $("#" + AJAX_ID) with $('table') it works -> demo (at least in Chrome)
and if I wrap the function inside a $(document).ready(function(){...}) it works in IE -> demo
I'm still looking to see what the problem might be, but I have a few comments about your code so far.
I haven't tested this, but I creating a jQuery object then appending another object inside ends up taking a lot of time because of the number of function calls. I've found it easier to just build up a string and only use one append. This example makes it easy to read:
var table = '\
<table style="width: 100%">\
<thead>\
<tr>\
<th>Numéro</th>\
<th>litre</th>\
<th style='width: 100px;'>Status</th>\
</tr>';
// append more to the string
table += '<tbody>.....</tbody></table>';
$('body').append(table);
I found this bit of code and I just wanted to show you that you can shorten it:
$("#no_batisse").css('display', 'none');
$("#lectures").html("");
$("#lectures").css('display', '');
shortens to:
$("#no_batisse").hide();
$("#lectures").empty().hide();
Instead of calling this function after each row addition, you could try adding a live function once that works with dynamically added content:
$(oLigne).find("input").blur(function() { onBlurLecture(oLigne); });
try running this when you initialize the script (just once)
$('#lecture').find('input').live('blur', function(){
onBlurLecture( $(this).closest('tr') );
})
I'll keep looking!
EDIT:
If your code is being triggered via some element that has a default behavior (like an <a> element), try adding return false; to the end of its callback.
Alternatively, if you give a parameter to the event handler's function, like function(e) {...}, you can call e.preventDefault() from within the callback instead of return false;.
First, I don't know if this is the issue, but IDs can not start with a number.
Second, which element has the AJAX ID that are you using? You'll get different results depending on that.
To avoid any ID issues, you could do:
$("#lectures").find('tr:last > td:nth-child(2) > input').focus();
or if you want it to be relative, do:
$("#" + AJAX_ID).closest('tbody').find('tr:last > td:nth-child(2) > input').focus();
got it!
I use "tab" (tabulation) to switch to the next input while writing in them. At the last one, the focus goes out of the window so I cannot set it. I converted the last column "status" into a input so it gets the focus while onBlur is executed. When the ajax load callback is called, the new line is added and the input is focused as it should.
Chrome: works
Firefox: works
IE8: works (with fix)
spent 1½ day on that lol
thanks everyone!
Try to trigger .focusin() (this was added in jQuery's 1.4) See documentation.

Categories

Resources