I am using event-delegation on n-number of rows, my older approach was binding each row with event the code looked something like this:
function getDiv (data) {
var div = $("<div class='theDiv'>");
div.click(function () {
console.log(data);
});
return div;
}
function getContainer() {
var i, container;
container = $("<div class='container'></div>");
for (i = 0 ; i < 1000 ; i++) {
container.append(getDiv(i));
}
return container;
}
$("body").append(getContainer());
Note: In this approach each row element (theDiv) is having access to their data.
Now the question is, I want to bind a single click on container and access the data, the event-delegation approach would look like this:
function getNewDiv (data) {
var div = $("<div class='theDiv'>");
return div;
}
function getNewContainer() {
var i, container;
container = $("<div class='container'></div>");
for (i = 0 ; i < 1000 ; i++) {
container.append(getNewDiv(i));
}
container.click (function (e) {
var targetElem = e.target;
console.dir(e);
if ($(targetElem).hasClass("theDiv")) {
console.log("row was clicked");
}
})
return container;
}
$("body").append(getNewContainer());
Now, how to access the data associated with each row?
As per my learning:
I can add the data to
data-*, but this would limit me to simple data type
$.data associated to element
Is there any other way todo this?
.on allows you to do event delegation:
container.on('click', '.theDiv', function () {
//`this` is `.theDiv`.
});
You can use $.data() inside getNewDiv() to store a reference index:
function getNewDiv (dataIndex) {
return $("<div class='theDiv'>").data('idx', dataIndex);
}
Then, use each element's data index variable to reference an object in your big array of data:
var mydata = [{ ... }, { ... }, { ... }];
container.on('click', '.theDiv', function () {
var data = mydata[$(this).data('idx')];
console.log("row was clicked");
});
Related
my javascript variables
var select1 = document.getElementById('status-kehadiran1');
select1.onchange = function () {
select1.classList.remove("hijau");
select1.classList.remove("merah");
select1.classList.add(this.options[this.selectedIndex].className);
}
var select2 = document.getElementById('status-kehadiran2');
select2.onchange = function () {
select2.classList.remove("hijau");
select2.classList.remove("merah");
select2.classList.add(this.options[this.selectedIndex].className);
}
var select3 = document.getElementById('status-kehadiran3');
select3.onchange = function () {
select3.classList.remove("hijau");
select3.classList.remove("merah");
select3.classList.add(this.options[this.selectedIndex].className);
}
var select4 = document.getElementById('status-kehadiran4');
select4.onchange = function () {
select4.classList.remove("hijau");
select4.classList.remove("merah");
select4.classList.add(this.options[this.selectedIndex].className);
}
var select5 = document.getElementById('status-kehadiran5');
select5.onchange = function () {
select5.classList.remove("hijau");
select5.classList.remove("merah");
select5.classList.add(this.options[this.selectedIndex].className);
}
var select6 = document.getElementById('status-kehadiran6');
select6.onchange = function () {
select6.classList.remove("hijau");
select6.classList.remove("merah");
select6.classList.add(this.options[this.selectedIndex].className);
}
var select7 = document.getElementById('status-kehadiran7');
select7.onchange = function () {
select7.classList.remove("hijau");
select7.classList.remove("merah");
select7.classList.add(this.options[this.selectedIndex].className);
}
var select8 = document.getElementById('status-kehadiran8');
select8.onchange = function () {
select8.classList.remove("hijau");
select8.classList.remove("merah");
select8.classList.add(this.options[this.selectedIndex].className);
}
var select9 = document.getElementById('status-kehadiran9');
select9.onchange = function () {
select9.classList.remove("hijau");
select9.classList.remove("merah");
select9.classList.add(this.options[this.selectedIndex].className);
}
var select10 = document.getElementById('status-kehadiran10');
select10.onchange = function () {
select10.classList.remove("hijau");
select10.classList.remove("merah");
select10.classList.add(this.options[this.selectedIndex].className);
}
is there a way to create a looping for those variable to make it more simple?
my html select elements
because i have 10 select element with different id
view
the purpose of each variable is to change the text color of each select when there is a change of selected option
i hope you can understand my explanation
Don't use IDs, use a common selector instead for all of those selects - such as [name^="pertemuan"] (name attribute starts with pertemuan):
for (const select of document.querySelectorAll('[name^="pertemuan"]')) {
select.addEventListener('change', () => {
select.classList.remove("hijau", "merah");
select.classList.add(select.options[select.selectedIndex].className);
});
}
You could drop the ids and move status-kehadiran to the selects class list. The you can select all of them with .querySelectorAll() and loop over them with .forEach()
document.querySelectorAll('.status-kehadiran')
.forEach(function(select) {
select.addEventListener('change', function() {
this.classList.remove('hijau', 'merah');
this.classList.add(this.selectedOptions[0].className);
});
});
As mentioned by others you can use query selector with name and ids, but you can also assign them a common class and use -
document.getElementsByClassName('.name of class')
.forEach(function(select) {
select.addEventListener('change', function() {
this.classList.remove('hijau', 'merah');
this.classList.add(this.selectedOptions[0].className);
});
});
You can first select all the elements with ids using Document.querySelectorAll() and then loop through them and attach the event (change) one by one using EventTarget.addEventListener().
You can try the following way:
//you can select by exact id
//var sel = document.querySelectorAll('#status-kehadiran1, #status-kehadiran2, #status-kehadiran3, #status-kehadiran4, #status-kehadiran5, #status-kehadiran6, #status-kehadiran7, #status-kehadiran8, #status-kehadiran9, #status-kehadiran10');
//you can select by id startsWith selector by matching the common part of each id
var sel = document.querySelectorAll('[id^=status-kehadiran]');
sel.forEach(function(el){
el.addEventListener('change', function(){
el.classList.remove("hijau", "merah"); //you can remove multiple class separating them with comma
el.classList.add(el.options[el.selectedIndex].className);
});
});
Is there a way to get all javascript associated with an html element by class name returned in an array? Any suggestion as to how one would achieve doing this? Are there any node packages that would allow me to do something like this?
For example:
HTML
<div class="click_me">Click Me</div>
JS
$('.click_me').on('click', function() { alert ('hi') });
I would want something like (psuedo-code either on the client or server side):
function meta() {
let js = [];
js = getAllJavascriptByClassName('click_me');
console.log(js[0]);
}
Output of meta()
$('.click_me').on('click', function() { alert ('hi') });
This will pull out all event handlers of all elements of given class.
But these handlers must be attached using jquery.
function getAllEventHandlersByClassName(className) {
var elements = $('.' + className);
var results = [];
for (var i = 0; i < elements.length; i++) {
var eventHandlers = $._data(elements[i], "events");
for (var j in eventHandlers) {
var handlers = [];
var event = j;
eventHandlers[event].forEach(function(handlerObj) {
handlers.push(handlerObj.handler.toString());
});
var result = {};
result[event] = handlers;
results.push(result);
}
}
return results;
}
// demo
$('.target').on('click',function(event){
alert('firstClick handler')
});
$('.target').on('click',function(event){
alert('secondClick handler')
});
$('.target').on('mousedown',function(event){
alert('firstClick handler')
});
console.log(getAllEventHandlersByClassName('target'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='target'> </div>
You can use getEventListeners() which is part of the chrome devtools but for employing client side, there's an possible-duplicate question that partially answers this: How to find event listeners on a DOM node when debugging or from the JavaScript code? which basically shows (in the second voted answer) that depending on how the events are set (javascript attribute, eventListener, jquery, other lib) there are different ways to retrieve the functions.
The Visual Event 2 program mentioned in the first question seems to be more of a library doing what the second answer is suggesting so maybe this will solve your problem.
If you are interested only in jQuery solution I may suggest you (I assume there is only one event per type, but you need to cycle on all instances):
function getAllJavascriptByClassName(className) {
var elem = $('.' + className);
var result = [];
$('.' + className).each(function(index, element) {
var resultObjs = jQuery._data(element, "events");
var partialResult = [];
var x = Object.keys(resultObjs).forEach(function(currentValue, index, array) {
partialResult.push(resultObjs[currentValue][0].handler.toString());
});
result.push(partialResult);
});
return result;
}
function meta() {
let js = [];
js = getAllJavascriptByClassName('click_me');
console.log(JSON.stringify(js, null, 4));
}
$(function () {
$('.click_me').on('click', function (e) {
alert('Click event: hi')
});
$('.click_me:last').on('keypress', function (e) {
alert('Keypress event: hi')
});
meta();
});
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<div class="click_me">Click Me</div>
<div class="click_me">Click Me</div>
I would personally override addEventListener at the right places (meaning at the very top) with some safe guards.
UNfortunately jquery event handlers appear to be quite hard to read...
var element = document.getElementById("zou");
element.addEventListener("click", function(e) {
console.log("clicked from addevent");
});
element.addEventListener("mouseup", function(e) {
console.log("mouseup from addevent");
});
$(element).on("mousedown", function(e) {
console.log("mousedown from $")
});
console.log(element.getListeners());
<script>
window.eventStorage = {};
(function() {
var old = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function(a, b, c) {
if (!window.eventStorage[this]) {
window.eventStorage[this] = [];
}
var val = {
"event": a,
"callback": b
};
var alreadyRegistered = false;
var arr = window.eventStorage[this];
for (var i = 0; i < arr.length; ++i) {
if (arr.event == a && arr.callback == b) {
alreadyRegistered = true;
break;
}
}
if (!alreadyRegistered) {
arr.push(val);
}
old.call(this, a, b, c);
}
HTMLElement.prototype.getListeners = function() {
return window.eventStorage[this] || {};
}
}());
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="zou">click on me</div>
I've got stuck here with jQuery remove method. Here's a piece of code ...
clearTemplate : function(contentContainer) {
var container = $('body').find('#' + contentContainer);
if (container.length !== 0) {
console.log(container.length);
container.remove();
console.log(container.length);
}
}
Script can easily find '#'+contentContainer in DOM but is unable to remove it. It has no problem with removing children elements of container object as well.
Console.log returns (obviously) : 1 and 1
Container is also dynamically loaded into DOM.
Here's a bigger piece ...
var TemplateClass = {
mainDiv : $('<div>').attr('id',contentContainer),
setData : function(result, images, template, link) {
this.result = result;
this.images = images;
this.template = template;
this.link = link;
},
readyTemplate : function() {
var that = this;
$.each(this.result, function () {
data = that.result[0];
$(that.mainDiv).loadTemplate(that.template, data, {
append: true
});
});
return this.mainDiv;
},
clearTemplate : function(contentContainer) {
var container = $('body').find('#' + contentContainer);
if (container.length !== 0) {
console.log(container.length);
container.remove();
console.log(container.length);
}
}
}
I'm able to live without removing this object, but it's just not right.
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 () {}
});
Here is I have table built with knockout 'foreach'. After user select some rows, these rows will contain class 'success'. So I want to use self.create event that fire after user press button (button located outside element that ViewModel binded to) in order to handle such table rows. But Firebug said: TypeError: GrafikViewModel.books is undefined.
Here is the code:
function InfoViewModel(baseUri) {
//some viewmodel here
}
//This is viewmodel I'm talking about.
function GrafikViewModel(grafikUri) {
var self = this;
self.books = ko.observableArray();
self.create = function () {
//Here we will handle tr with class 'success'
alert("!!!");
}
$.getJSON(grafikUri, function (data) {
self.books(data.$values);
});
}
$(document).ready(function () {
var url = location.href.split("/");
var baseUri;
var tkod = url[5];
if (url[4].toString = 'x') {
baseUri = '/api/xTourist/' + tkod;
}
else if (url[4].toString = 'y') {
baseUri = '/api/yTourist/' + tkod;
}
var grafikUri = '/api/grafik/' + tkod;
ko.applyBindings(InfoViewModel(baseUri), document.getElementById('info'));
ko.applyBindings(new GrafikViewModel(grafikUri), document.getElementById('grafik'));
$('#book').click(function () {
//Here I'm trying to call ViewModel.
GrafikViewModel.books.create(ko.dataFor(this));
});
});
try new GrafikViewModel().books.create(ko.dataFor(this));