jQuery .change() event is only fired once - javascript

I have an application that retrieves Project names from a database when the DOM is ready. Each Project is added to a <select><option> in an html <form>. Once the list is populated the user can select a project title, which will request the remaining information from the database specific to that project.
To achieve this I'm using the $.change() jQuery method. Unfortunately, the event is only fired once when the <select> element is created and added to the DOM. Selecting another project from the list does not fire the event, and therefore does not trigger a $.post() call.
$(function(){
getProjects();
var firstLoad = true;
$("select").change(retrieveProject); // Removed parenthesis based on answers
// On page load, get project names from the database and add them to a Select form element
function getProjects() {
var selectionList;
$.getJSON("php/getProjects.php", function (data) {
selectionList = "<form><select>";
for (var i = 0; i < data.length; i++) {
selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
}
selectionList += "</select></form>";
}).complete(function() {
$('#project-selection-menu').append(selectionList).removeClass('hidden');
firstLoad = false;
});
}
function retrieveProject() {
if ( firstLoad == true ){
alert(firstLoad); // This alert fires
return false;
} else {
alert(firstLoad); // This alert doesn't fire
$.post("php/getProjects.php", function (data) { // This should have been "php/retrieveProject.php"!
// Do stuff with the returned data
}).complete(function() {
console.log("Success.");
});
}
}
)};

You're not setting up the event handler properly:
$("select").change(retrieveProject);
In your code, you were calling the "retrieveProject" function, and the return value from that function call was being passed as the "change" handler (and of course having no effect). That's why it appeared that the event was being generated upon page load.
When you're working with a function as a value, you don't use () after the function reference — it's the reference itself (the function name, in this case) that you want. That's what needs to be passed to jQuery.
Also — and this is important — make sure that your code is run either in a "ready" or "load" handler, or else that your <script> comes after the <select> element on the page. If the script is in the document head, then it'll run before the DOM is parsed, and it'll have no effect. (Another way to deal with that would be to use an .on() delegated form as suggested in another answer.)
More: it looks like you're overwriting your <select> element when you fetch the content in "getProjects". Thus, you should definitely use the delegated form:
$(document).on("change", "select", retrieveProject);
Also, you should be using local variables in "getProjects":
function getProjects() {
var selectionList; // keep this local to the function - implicit globals are risky
$.getJSON("php/getProjects.php", function (data) {
selectionList = "<form><select>";
for (var i = 0; i < data.length; i++) {
selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
}
selectionList += "</select></form>";
}).complete(function() {
$('#project-selection-menu').append(selectionList).removeClass('hidden');
firstLoad = false;
});
}

You need to handle event delegation
$(document).on('change', 'select', retrieveProject);
Also remove () next to the method retrieveProject

you can also do this by using following which will work fine.
$("select").change(function(){retrieveProject()});
or
$("select").on('change',function(){retrieveProject()});

Is this what your looking for? To run getProjects once the page loads just call it in your $(document).ready() function. Also you need to properly setup your change handler. See the fiddle for reference.
var firstLoad = true;
getProjects();
$("#selectTest").change(function(){
retrieveProject();
});
// On page load, get project names from the database and add them to a Select form element
function getProjects() {
$.getJSON("php/getProjects.php", function (data) {
selectionList = "<form><select>";
for (var i = 0; i < data.length; i++) {
selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
}
selectionList += "</select></form>";
}).complete(function() {
$('#project-selection-menu').append(selectionList).removeClass('hidden');
firstLoad = false;
});
}
function retrieveProject() {
if ( firstLoad == true ){
alert(firstLoad); // This alert fires
return false;
} else {
alert(firstLoad); // This alert doesn't fire
$.post("php/getProjects.php", function (data) {
// Do stuff with the returned data
}).complete(function() {
console.log("Success.");
});
}
}
http://jsfiddle.net/trevordowdle/Mf38E/

Try this:
$(document).bind('change', 'select', function(){
retrieveProject();
});

Related

Onclick event for "li" in a for loop when using createElement

I'm trying to add an event listener (onclick) onto every li element that gets created in a specific for loop - using JavaScript.
First I tried using tempLi.onclick (see code below for context), but it wouldn't run the function. After that I searched for the issue here on Stackoverflow, and I read that this method I'm using below should work - but it doesn't (not in my case at least).
if (users.length !== 0) {
for (let i = 0; i < users.length; i++) {
let tempLi = d.createElement('li');
tempLi.className = 'btn btn-primary knappur'
tempLi.innerHTML = users[i];
getId('usersUl').appendChild(tempLi);
(function(value) {
tempLi.addEventListener("click", function() {
alert(value);
}, false);
})(users[i]);
getId('usersUl').innerHTML += '<br>';
}
}
The code is in a function called loginPrepare:
let loginPrepare = () => { ... }
How can I execute code when I click on the generated li (tempLi)?
EDIT: The code that I'd like to run when clicked on the "li" is login(users[i])
I would put the event handler on your ul and rely on event bubbling instead of attaching an event handler to each. It would look something like this.
document.getElementById('usersUl')
.addEventListener('click', function(e) {
if (e.target.hasClass('knappur') {
// Do your work
}
});
You are creating a function inside a loop, referencing the ever-changing index i, which, when the loop is done might be a completely different value than what you think it is.
Also, I think this way you are attaching the event-listener before the tempLi has had time to be properly integrated into the dom.
Third: you don't need to create a new event-listener for each list item. One is enough.//
Try this instead:
if (users.length !== 0) {
const list = getId('usersUl')
for (let i = 0; i < users.length; i++) {
let tempLi = d.createElement('li');
tempLi.className = 'btn btn-primary knappur';
tempLi.innerHTML = users[i];
tempLi.setAttribute('data-user', users[i])
list.appendChild(tempLi);
//list.innerHTML += '<br>'; don't do this btw, <br />s aren't valid children of lists!
}
list.addEventListener("click", event => {
const user = event.target.getAttribute('data-user');
alert(user);
});
}
We can achieve event binding to HTML in very simple way using JQuery like below:
$("#usersUl").on('click',function(e){
//what we do
});

How can I modify an anonymous javascript function with tampermonkey?

Here is the block of code I want to replace:
$(document).ready(function () {
$(".button-purple").click(function () {
interval = $(this).attr('id');
name = $(this.attr('name');
if(Number($(this).val()) === 0) {
if(name == 'static') {
do this
}
else {
do this
}
}
else {
do this
}
});
});
I can't find any documentation on trying to replace the function since it's unnamed though. Is it possible to replace the entire javascript file + delete the line loading it / insert my own script? Would really appreciate any help I can get.
If you just want to remove the click event handler, then simply say
var $element = $(".button-purple");
$element.off('click');
If you want to Remove all the event handlers, then you'll first have to find out what all event handlers are present and then remove them iteratively.
var element = $element[0]; //Make sure the element is a DOM object and not jQuery Object.
// Use this line if you're using jQuery 1.8+
var attachedEvents = $._data(element,'events');
// Use this line if you're using jQuery < 1.8
var attachedEvents = $(element).data('events'); //Here you can also replace $(element) with $element as declared above.
for(var event in attachedEvents){
$element.off(event);
}
UPDATE:
You can simply add your own event handler (using .on() API) after you're done removing all the required existing handlers.
Just define your function.
function yourFunction(){ /* your code */};
$element.on('click', yourFunction);
Update 2:
Since you just want to remove the click event handler, this is the simplest code that will serve your purpose.
$(".button-purple").off('click').on('click', yourFunction);
I'm not aware of tampermonkey, but you can try this:
function chickHandler() {
interval = $(this).attr('id');
name = $(this.attr('name');
if (Number($(this).val()) === 0) {
if (name == 'static') {
do this
} else {
do this
}
} else {
do this
}
}
}
function onReadyHandler() {
$(".button-purple").click(chickHandler);
}
$(document).ready(onReadyHandler);
When you do something like .click(function(){...}), here function is called as a callback. You have to send a function as a callback. Not necessary to be anonymous.

how to make the jquery function load before one ajax function finish

How do I fire one event before the previous function completed its function?
I have the following AJAX code :
var BrainyFilter = {
//...
init: function (opts) {},
changeTotalNumbers: function (data) {
jQuery(BrainyFilter.filterFormId).find('.bf-count').remove();
jQuery(BrainyFilter.filterFormId).find('option span').remove();
jQuery(BrainyFilter.filterFormId).find('select').removeAttr('disabled');
jQuery('.bf-attr-filter').not('#bf-price-container').find('input, option')
.attr('disabled', 'disabled')
.parents('.bf-attr-filter')
.addClass('bf-disabled');
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
jQuery('.bf-attr-' + data[i].id + ' .bf-attr-val').each(function (ii, v) {
if (jQuery(v).text() == data[i].val) {
var parent = jQuery(v).parents('.bf-attr-filter').eq(0);
var isOption = jQuery(v).prop('tagName') == 'OPTION';
var selected = false;
if (isOption) {
jQuery(v).removeAttr('disabled');
selected = jQuery(v)[0].selected;
} else {
parent.find('input').removeAttr('disabled');
selected = parent.find('input')[0].checked;
}
parent.removeClass('bf-disabled');
if (!selected) {
if (!isOption) {
parent.find('.bf-cell').last().append('<span class="bf-count">' + data[i].c + '</span>');
} else {
jQuery(v).append('<span> (' + data[i].c + ')</span>');
}
}
}
});
}
jQuery('.bf-attr-filter input[type=checkbox]').filter(':checked')
.parents('.bf-attr-block').find('.bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text();
jQuery(v).text(t);
});
// since opencart standard filters use logical OR, all the filter groups
// should have '+' if any filter was selected
if (jQuery('.bf-opencart-filters input[type=checkbox]:checked').size()) {
jQuery('.bf-opencart-filters .bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text().replace('+', '');
jQuery(v).text(t);
});
}
}
// disable select box if it hasn't any active option
jQuery(BrainyFilter.filterFormId).find('select').each(function (i, v) {
if (jQuery(v).find('option').not('.bf-default,[disabled]').size() == 0) {
jQuery(v).attr('disabled', 'true');
}
});
},
//...
} // close the BrainyFilter
I also have another jQuery file running to get the bf-count value using $('.bf-count').text().
When the page load, the bf-count value is empty. Since the code above inject the bf-count, I will need to wait until it finishes the for loop in order to get the bf-count value.
What is the best way to approach this?
without knowing how the second js file is loaded, I can only give you a guesstimate suggestion.
If you want to run the second js file code after the page is fully loaded, you can wrap the code in:
jQuery(window).load(function(){
//your code here. runs after the page is fully loaded
});
jQuery documentation: http://api.jquery.com/load-event/
"The load event is sent to an element when it and all sub-elements have been completely loaded. This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object."

Binding to content change in body with jQuery

I have a function from icomoon that runs when the window loads (see below).
I would like to change it so that the function gets called once when the document loads, and then if there are any subsequent changes to the body - eg: from js/ajax (and preferably then only on the changed part of the dom, so as not to loop through the entire document again and again). Any suggestions on what jquery on events I should use for this, and then to only check the changes once the first execution on the entire document has been completed? Needs to be Ie7+ compatible too.
Thanks much.
$( window ).load(function() {
function addIcon(el, entity) {
$(el).addClass("iconed");
var html = el.innerHTML;
el.innerHTML = '<span style="font-family: \'icomoon\'">' + entity + '</span>' + html;
}
var icons = {
.....
};
function iconify() {
var els = document.getElementsByTagName('*'),
i, attr, c, el;
for (i = 0; ; i += 1) {
el = els[i];
if(!el) {
break;
}
attr = el.getAttribute('data-icon');
if (attr) {
if (!$(el).hasClass("iconed")) {
addIcon(el, attr);
}
}
c = el.className;
c = c.match(/icon-[^\s'"]+/);
if (c && icons[c[0]]) {
if (!$(el).hasClass("iconed")) {
addIcon(el, icons[c[0]]);
}
}
}
}
iconify();
$('body').on("contentchanged", function() { //some event that triggers ONCE the document has fully loaded, and is triggered when the DOM changes..
iconify(); //would prefer if this function only checked the modified part of the DOM - rather than the entire DOM each time (except on the 1st execution - when window loaded.
});
});
You can fire custom events when the content of body is changed by ajax or any other function which you are aware of . And then bind a custom function to that event.
Let's say in
function addIcon(el, entity) {
$(el).addClass("iconed");
var html = el.innerHTML;
el.innerHTML = '<span style="font-family: \'icomoon\'">' + entity + '</span>' + html;
$(document).trigger('contentchanged');
}
And then you can write a custom function handling this event like:
$(document).on('contentchange','selector',function(){
//Your code goes here.
});
You can also refer for DOM mutation events

Google Apps Script Find function caller id

I have a Google Apps Script that dynamically generates buttons and assigns for each a ClickHandler which in turn calls a function.
My problem is that because every button calls the same function I can't find a way to indentify which of them actually made the call. Here is a code sample:
var handler = app.createServerHandler("buttonAction");
for (i=1,...) {
app.createButton(...).setId(i).addClickHandler(handler);
}
function buttonAction() {
//How do I know what button made the call?
}
Another option is to use the e.parameter.source value to determine the ID of the element that triggered the serverHandler to be called.
Here's an example:
function doGet(e) {
var app = UiApp.createApplication();
var handler = app.createServerHandler("buttonAction");
for (var i = 0; i < 4; i++) {
app.add(app.createButton('button'+i).setId(i).addClickHandler(handler));
}
return app;
}
function buttonAction(e) {
var app = UiApp.getActiveApplication();
Logger.log(e.parameter.source);
}
e.parameter.source will contain the ID of the element, which you could then use to call app.getElementById(e.parameter.source) ...
You could create multiple handlers, each for one button:
for (i=1,...) {
var handler = app.createServerHandler("buttonAction" + i);
app.createButton(...).setId(i).addClickHandler(handler);
}
function buttonAction1() {
// code to handle button 1
}
function buttonAction2() {
// code to handle button 2
}
function buttonAction...
I wouldn't recommend of having these sort of "anonymous" action handlers though, as you might be having troubles later in remembering which actionX does what.
(e.g. have a different approach, w/o a loop, or prepare a dictionary-like/array object of meaningful handler names before that loop.)
OTOH, you could use event object argument provided to your callback function:
function buttonAction(event) {
// use event object here to identify where this event came from
}
The thing is the above event object properties depends on where your callback is being called from. For instance, if it were a submit button where you had a Form, then you could access parameters submitted by that from like so: event.parameter.myParamName. See code sample here.
So, if you have a variable number of buttons, you could use a hidden element + the button:
for (i=1,...) {
var hiddenAction = app.createHidden("action", "action"+i);
var handler = app.createServerHandler("buttonAction");
handler.addCallbackElement(hiddenAction);
var btn = app.createButton("Button text", handler);
// you'll need to add both btn and hidden field
// to the UI
app.add(hiddenAction);
app.add(btn);
}
Then, your buttonAction might look like this:
function buttonAction(e) {
var action = e.parameter.action;
// do something based on action value here
// which will be one of "action1", "action2", ...
}
The above is a copy & paste from Hidden class sample.
The above might not work out of the box, but you get the idea: create a hidden element that holds the info you need in your callback, and attach that hidden to your server handler. You could even create multiple hidden elements or a Form panel.
I have the same issue. It works using Tag.
EG
SETUP
var button = addButton(app
,panel
,"buttonActiveTrelloProjects_" + i.toString()
,appVars.buttonWidth() + "px"
,appVars.level2ButtonHeight().toString() + "px"
,false
,false
,"Trello"
,"buttonActiveTrelloProjectsHandler"
,(appVars.buttonLhsGap() * buttonCntr) + (appVars.buttonWidth() * (buttonCntr - 1 ) + 9)
,(appVars.level2ButtonTopGap() * 34)
,3
,"button");
button.setTag(projectName );
USE
function buttonActiveProjectsChartHandler_1(button){
...
buttonTag = getButtonTag(button);
chartType = buttonTag.split(";")[1];
activeProject = buttonTag.split(";")[0];
...
}
function getButtonTag(button){
var jsonButton = JSON.stringify(button);
var source = button.parameter.source;
var tagPtr = source + "_tag";
return button.parameter[tagPtr];
}

Categories

Resources