YUI after appending body, on('click') even doesn't work - javascript

Hello I have been trying to build my DOM with html templates and js, but i had problem with this code.;
YUI().use(
'aui-modal',
'stylesheet',
'aui-io-request',
function(Y) {
var officers;
Y.io.request('https://demo6120867.mockable.io/cmdOfficers', {
method: 'GET',
dataType: 'json',
on: {
success:function() {
officers = this.get('responseData').officers;
for(var i=0;i<officers.length;i++){
var template = Y.one("#officerTemplate")
var officerName = template.one("#officerName");
var officerDepartment = template.one("#department");
var officerId;
officerName.set('innerText',officers[i].officerName);
officerDepartment.set('innerText',officers[i].department);
var buttonHolder = template.one("#action_buttons")
var officerButtons = buttonHolder.all(".hp");
console.log("Officer Buttons")
console.log(officerButtons[0]);
console.log("Officer Buttons all");
console.log(officerButtons);
officerButtons.each(function(buttonNode,index){
console.log(buttonNode);
officerId = officers[i].id;
var data = buttonNode.getData("id");
// This will not get store in dom.
buttonNode.setData("id",i+"_"+index);
buttonNode.setData("officer_id",officerId);
console.log(buttonNode.getData("id"));
});
var bodyNode = Y.one(document.body);
var newItem = Y.Node.create(template.getContent()).setStyle("display","block");;
newItem.set('id','officer_'+i);
bodyNode.append(newItem);
// when we have template we can insert our data and append to dom.
}
}
}
});
The last line of code as you see is appending body so i am expecting when i call
Y.all(".hp").on("click",function(e){
console.log("clicked");
click function doesn't work, how can i workaround this problem.
Thanks.

Looks like this might be a perfect case for event delegation. Since the elements in questions are not in the DOM yet when the event is bound.
Y.one(document.body).delegate("click", function(e) {
console.log("clicked");
}, ".hp");
The document body can be replaced with the closest parent that contains the .hp elements.
https://davidwalsh.name/event-delegate

Related

How can I pass a function to click handler in the correct context?

I have to get an argument value from the function inside the onlick property of a button, but the function (this.getListItemId(itemId)) which makes use of this argument is not inside of the context from where the button is. The button is inside a child function
(this.renderVideoListTemplate()) which is inside a parent function (getVideoListItems()). The function i want call on the onclick event (this.getListItemId()) it is outside from where the function of the button (this.renderVideoListTemplate()) is, so it says the function (this.getListItemId(itemId)) does not exists.
var getVideoListItems = function() {
var self = this;
this.renderVideoListTemplate = function(result) {
for(let i=0; i < result.length; i++){
====>>> var listItems ="<div class='Button fecharVideo' onclick='"+self.getListItemId(result[i].ID)+"'>"+
"<span class='Button'>Excluir Vídeo</span>"+
"</div>";
$("#video-list-container").append(listItems);
};
};
this.deleteListItem = function(webUrl, listName, itemId, success, failure) {
self.getDataByItemId(webUrl,listName,itemId,function(item){
$.ajax({
url: item.__metadata.uri,
type: "POST",
headers: {
"Accept": "application/json;odata=verbose",
"X-Http-Method": "DELETE",
"If-Match": item.__metadata.etag
},
success: function (data) {
success();
},
error: function (data) {
failure(data.responseJSON.error);
}
});
},
function (error) {
failure(error);
});
};
this.getListItemId = function(itemId){
self.deleteListItem(webUrl, listName, itemId)
};
return this.init();
}
You cannot assign a function reference within the string value of an onclick attribute.
You could avoid the issue and improve the logic by instead using a delegated event handler. You can keep the result[i].ID in a data attribute on the div you create which can be read when the click event happens. Try this:
// in your object:
this.renderVideoListTemplate = function(result) {
var divs = result.map(function() {
return `<div class="Button fecharVideo" data-id="${this.ID}"><span class="Button">Excluir Vídeo</span></div>`
});
$("#video-list-container").append(divs);
};
// somewhere else in your codebase:
$('#video-list-container').on('click', '.fecharVideo', function(e) {
e.preventDefault();
yourObjectReferece.getListItemId($(this).data('id'));
})
Also note the use of map() to build an array of strings to append to the #video-list-container element once, instead of within every iteration.
First of all when witing:
<a href'#' onclick='this.getListItemId(itemId)'>...
this. will refer to the Object(HTMLElement) a who has the function.
Second:
Best way to use listener is to use the .addEventListener FUNCTION such:
document.querySelector('#id_of_element').addEventListener('click', function(e){
//-- do something with e arg is the event
})
Using element.addEventListener(... separate code from the HTML , way clearer and easy to manipulate and use.
By the way use named function allow you to removeEventListener that will erase behaviour and lighten code way to go.
I see you are using jQuery here, instead of using simple string you can create a jQuery element and add a listener with $().on()
So, this for loop:
for(let i=0; i < result.length; i++){
====>>> var listItems ="<div class='Button fecharVideo' onclick='"+self.getListItemId(result[i].ID)+"'>"+
"<span class='Button'>Excluir Vídeo</span>"+
"</div>";
$("#video-list-container").append(listItems);
};
can be transformed in the following one:
for(let i=0; i < result.length; i++){
var listItems = $("<div class='Button fecharVideo'><span class='Button'>Excluir Vídeo</span></div>");
listItems.on('click', function (){
self.getListItemId(result[i].ID);
});
$("#video-list-container").append(listItems);
};

on() click to bind event to AJAX returned elements [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 8 years ago.
I've just read some threads about using on() to attach events to dynamically created html elements. I came up with this example (FIDDLE) to try to bind the click event to elements div.clicktitle returned via AJAX. The div elements have data attributes from which the click event should get the values and then display them. But It doesn't seem to be working.
What is working for me is to define the click event function and put it in the Ajax success callback. (FIDDLE) Is that the only way to make the click event to work with AJAX returned elements? Can anyone tell me why .on() isn't attaching the event to div.clicktitle?
HTML before Ajax
<div class="main"></div>
<div class="second"></div>
After Ajax it should be
<div class="main"><div data-source="Hello" class="clicktitle"><h6>Title1</h6></div></div>
<div class="second"><div data-source="Hello" class="clicktitle"><h6>Title2</h6></div></div>
JS Code:
$.ajax({
url: "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20rss%20where%20url%3D%22https%3A%2F%2Fnews.google.com%2F%3Foutput%3Drss%22&format=json&diagnostics=true&callback=",
success: function (data) {
var length = 0;
var items = data.query.results.item;
items = items.map(function(item){
return '<div data-source="Hello" class="clicktitle"><h6 style="background:beige;">'+item.title+'</h6></div>';
});
var half = items.length/2;
for(var i = 0; i < items.length; i++)
if(i < half)
$('.main').append(items[i]);
else
$('.second').append(items[i]);
length++;
},
error: function () {}
});
$('.clicktitle').on("click",function(){
var datasource = $(this).data('source');
$(this).text(datasource);
});
This should do it for you:
$('div').on("click",'.clicktitle',function(){
var datasource = $(this).data('source');
$(this).text(datasource);
});
the problem is that you try to add the listener directly to the generated element.
You have to add it to the parent element and use the filter in the on function like this :
$.ajax({
url: "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20rss%20where%20url%3D%22https%3A%2F%2Fnews.google.com%2F%3Foutput%3Drss%22&format=json&diagnostics=true&callback=",
success: function (data) {
var length = 0;
var items = data.query.results.item;
items = items.map(function(item){
return '<div data-source="Hello" class="clicktitle"><h6 style="background:beige;">'+item.title+'</h6></div>';
});
var half = items.length/2;
for(var i = 0; i < items.length; i++)
if(i < half)
$('.main').append(items[i]);
else
$('.second').append(items[i]);
length++;
},
error: function () {}
});
$('.main, .second').on("click",".clicktitle",function(){
var datasource = $(this).data('source');
$(this).text(datasource);
});
JSFIDDLE
hope this cut help you
Your binding won't work because your element doesn't exist yet, if you take #Alen's suggestion, that should do it for you, but you'll evaluate the handler EVERYTIME a click is made anywhere.
What you could do is attach the handler to the element before you write it to the DOM.
// Reusable event bheaviour
function clickBinding = function( event ){
var $elem = $(event.target),
datasource = $(this).data('source');
$elem.text(datasource);
}
// format item
function formatItem (item){
//setup elems
var h6 = document.createElement('h6'),
clickTitle = document.createElement('div');
// config h6
h6.style.background = 'beige';
h6.innerHTML = item.title;
clickTitle.appendChild(h6);
// config clickTitle
clickTitle.className = 'clicktitle';
clicktitle.setAttribute('data-source', 'Hello');
// Add event listeners
if (clicktitle.addEventListener) {
clicktitle.addEventListener ('click', clickBinding, false);
} else if (clicktitle.attachEvent) {
clicktitle.attachEvent ('onclick', clickBinding);
}
// return elem
return clicktitle;
}
$.ajax({
url: "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20rss%20where%20url%3D%22https%3A%2F%2Fnews.google.com%2F%3Foutput%3Drss%22&format=json&diagnostics=true&callback=",
success: function (data) {
var length = 0;
var items = data.query.results.item;
items = items.map(function(item){
return formatItem(item);
});
var half = items.length/2;
for(var i = 0; i < items.length; i++)
if(i < half)
$('.main').append(items[i]);
else
$('.second').append(items[i]);
length++;
},
error: function () {}
});

How to get the text of a textarea with a prototype click event?

There are several textareas on my page and I need the text of the one someone is clicking on. I would know how to do it in jQuery but I need a prototype/javascript solution. What I tried so far:
$$("textarea").each(function (el) {
el.observe('click', respondToClick);
function respondToClick(event) {
var element = Event.element(event);
var text = element.innerHTML();
console.log(text);
}
});
There are no errors in the console but also not the text I need. So how to get it?
EDIT:
That's the solution. I forgot the document.ready-equivalent in prototype and thanks to bruchowski I could edit the correct prototype method to get the text:
document.observe("dom:loaded", function () {
$$("textarea").each(function (el) {
el.observe('click', respondToClick);
function respondToClick(event) {
var element = Event.element(event);
var text = element.value;
console.log(text);
}
});
});
P.S.: Prototype Vesion is 1.7.0
You can use Form.Element.getValue(), or its $F() alias:
var element = Event.element(event);
var text = $F(element);
console.log(text);

JavaScript/jQuery dynamic function binding and ajax

I'm trying to build out a dynamic list of UI elements. I can hard code the UI list now, and everything works fine. I want this to be extensible however, so I'm trying to figure a way of passing id tags and corresponding functions to be called. Essentially, the callback function for my ajax call isn't being executed, though I am posting the correct data, and receiving the correct response. Here's the code:
myModule = function () {
var titleUI = Object.create(ChainUI());
var memcachedId = '<?php echo $memcachedId;?>';
return {
printChain: function(data) {
alert("printchain");
var territories = jQuery.parseJSON(data);
titleUI.makeChainTable();
territories.forEach(function(territory) {
titleUI.territoryDisplay(territory);
});
titleUI.tableDecorator($('#chainTable'));
},
loadChain: function() {
titleUI.destroyChainTable();
var url = 'traffichandler.php';
var instruction = {'instruction': 'titleChain', 'method': 'printChain', 'memcachedId': memcachedId.toString()};
$.post(url, instruction, this.printChain);
},
loadDefault: function() {
titleUI.loadUI(['Title Chain'], [this.loadChain]);
}
};
}();
The corresponding titleUI code follows:
var ChainUI = function() {
return {
loadUI: function(uiList, funcList) {
$('#uiFunctions').empty();
for(var i in uiList) {
var toDom = '<li id="'+uiList[i]+'">';
toDom += uiList[i];
toDom += '</li>';
$('#uiFunctions').html(toDom);
}
for(var i = 0; i < uiList.length; i++) {
var myFunc = funcList[i];
$('#uiFunctions').delegate($('#'+uiList[i]), "click",
function() {myFunc();}
);
}
},
}
In myModule.loadDefault, I can get this.loadChain to fire. I can't get this.printChain inside the $.post to work though. If remove all the dynamic stuff this.printChain works no problem. Don't get too hung up on the syntax, as is, the syntax is fine on my end. How can I get the ajax call back to work? Thanks!

Tried to register widget with id==valores0 but that id is already registered

i get this error, and i don't know how can be solved. I read this link before.
EDIT:1
index.php
<script type="text/javascript">
$(document).ready(function() {
$("#customForm").submit(function() {
var formdata = $("#customForm").serializeArray();
$.ajax({
url: "sent.php",
type: "post",
dataType: "json",
data: formdata,
success: function(data) {
switch (data.livre) {
case 'tags':
$("#msgbox2").fadeTo(200, 0.1, function() {
$(this).html('Empty tags').fadeTo(900, 1);
});
break;
default:
$("#msgbox2").fadeTo(200, 0.1, function() {
$(this).html('Update').fadeTo(900, 1, function() {
$('#conteudo').load('dojo/test_Slider.php');
});
});
break;
}
}
});
return false;
});
});
</script>
test_slider.php
<script type="text/javascript">
var slider = [];
for (i = 0; i < 5; i++) {
slider[i] = (
function(i) {
return function() {
var node = dojo.byId("input"+[i]);
var n = dojo.byId("valores"+[i]);
var rulesNode = document.createElement('div'+[i]);
node.appendChild(rulesNode);
var sliderRules = new dijit.form.HorizontalRule({
count:11,
style:{height:"4px"}
},rulesNode);
var labels = new dijit.form.HorizontalRuleLabels({
style:{height:"1em",fontSize:"75%"},
},n);
var theSlider = new dijit.form.HorizontalSlider({
value:5,
onChange: function(){
console.log(arguments);
},
name:"input"+[i],
onChange:function(val){ dojo.byId('value'+[i]).value = dojo.number.format(1/val,{places:4})},
style:{height:"165px"},
minimum:1,
maximum:9,
}
},node);
theSlider.startup();
sliderRules.startup();
}
})(i);
dojo.addOnLoad(slider[i]);
}
</script>
Problem: First click in submit btn all works well, 5 sliders are imported. Second click, an update is supposed, but i get this message:
Tried to register widget with id==valores0 but that id is already registered
[Demo video]2
Just to add on to #missingo's answer and #Kevin's comment. You could walk through the existing dijits by looking in the registry:
var i = i || 0; // Cache this at the end of your loop
dijit.registry.map(function (widget) {
if (+widget.id.replace(/^[^\d]+/, '') < i) {
widget.destroyRecursive();
}
});
/*
Your loop fixed as described in missingno's answer.
*/
You fell in the age-old trap of making function closures inside a for loop. By the time addOnLoad fires and the sliders are created, i will be equal to 2 and both sliders will try to use the same DOM nodes (something that is not allowed).
You need to make sure that you give a fresh copy of i for everyone. The following is a quick fix:
for(i=0; i<2; i++){
(function(i){
slider[i] = ...
//everything inside here remains the same
//except that they now use their own i from the wrapper function
//instead of sharing the i from outside.
}(i));
}
Dijit stores all active widgets in the dijit.registry, and uses id's as unique qualifiers. You can't create dijits with same id.
Need to clean dojo.registry before create a new slider dijits. Add this code before declare dijit on test_slider.php
dijit.registry["input"+ [i]].destroyRecursive();
can you assign any number ID like ID generated by 10 digit random number or something with datetime combination so id will never be same.

Categories