How to create an hyperlink whose onclick handler is an anonymous function? - javascript

I am trying to generate dynamically the onclick event handlers of the cells of a flexigrid-generated table:
// ...
preProcess: function (data) {
var rows = data.rows;
for (var i = 0; i < rows.length; ++i) {
var row = rows[i];
// If and only if this condition is true, then
// row.cell[0] must be converted into a hyperlink.
if (row.cell[1] != '0') {
// I don't want to use the href attribute, because that would
// force me to define a non-anonymous function.
row.cell[0] = '<a href="javascript:void(0)" id="E'
+ i + '">' + row.cell[0] + '</a>';
// So I'm going to try assigning the onclick attribute.
$('#E' + i).click(function () {
window.open('doc.php?q=' + this.id, 'D' + this.id,
'menubar=0,toolbar=0,directories=0,location=0,status=0,' +
'resizable=0,scrollbars=0,width=600,height=300');
});
$('#E' + i).click().id = row.cell[4];
}
}
return data;
}
// ...
However, when I click on the generated hyperlinks, they don't work. What's the problem? My use of closures? The <a> tag doesn't accept the onclick attribute?
NOTE: Since I began using jQuery, my policy is all functions shall be anonymous functions. Please don't suggest me using an ordinary function.

Sounds like what you're looking for is live():
Attach a handler to the event for all elements which match the current selector, now and in the future
In effect, it allows you to create event handlers for elements that do not exist yet.
I get the feeling you only want to make minimal changes to your current code in order to make this work. In that case, live() is your best option since your code would only change from
$('#E' + i).click(function () { ...
to
$('#E' + i).live('click', function () { ...

Create the element using jQuery (or the browser's native dom functions) and attach an event handler:
$('<a href="#" id="E' + i + '"/>').html(row.cell[0]).click(function(e) {
e.preventDefault();
// your code
});

It looks like you're creating the <a> using raw string concatenation, and then assigning it... where? If the link isn't part of the DOM, then $('linkID') won't find anything, effectively assigning your click handler to nothing. jQuery selectors only search the DOM.

Firstly, it doesn't look like you're appending your with id='#E' + i.
So, I'd guess that when you call $('#E' + i), it's returning an empty jQuery object. You can check for this by alerting $('#E' + i).length. 0 means nothing was found.
Second, you don't need to the javascript:void(0) call. Just replace it with '#' and call event.preventDefault() in your anonymous function. You'll need to pass event as a parameter to the anonymous function, as well.

You are trying to hook up the onclick event on an element that doesn't exist yet. At the time, the element only exist as text in the array, as the code hasn't been added to the DOM, the selector can't find it.
If you want to use an anonymous function for the event handler, you have to wait to hook up the event until the element has been created so that it exists as an object.

Use jQuery's live event.
For ease of seeing what's going on, I'm also adding a class to the link because I'm assuming that there's other links on the page, .
function preProcess(data) {
...
row.cell[0] = '' + row.cell[0] + '';
}
jQuery("a.clickMe").live("click", function(event) {
event.preventDefault();
window.open('doc.php?q=' + this.id, 'D' + this.id, .....
});
Disclaimer: I've never used flexigrid, but from your other comments, it appears you are able to modify the content before flexigrid puts it in the DOM.
The live event lets up hook up a single handler (anonymous or not) before the element is added to the DOM.
See: jQuery live()
.live()
Attach a handler to the event
for all elements which match the
current selector, now and in the
future

I copied your code and, after a few minor corrections, I made it work. I assumed that data was referring to a table object. Here's my code together with dummy HTML.
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
</head>
<body>
<table id='myTable'>
<tr>
<td>x</td><td>1</td><td>a</td><td>f</td><td>p</td>
</tr>
<tr>
<td>y</td><td>2</td><td>b</td><td>g</td><td>q</td>
</tr>
</table>
<script>
function preProcess(data) {
var rows = data.rows;
for (var i = 0; i < rows.length; ++i) {
var row = rows[i];
// If and only if this condition is true, then
// row.cell[0] must be converted into a hyperlink.
if (row.cells[1] != '0') {
// I don't want to use the href attribute, because that would
// force me to define a non-anonymous function.
row.cells[0].innerHTML = '<a href="javascript:void(0)" id="E' + i + '">'
+ row.cells[0].innerHTML + '</a>';
// So I'm going to try assigning the onclick attribute.
$('#E' + i).click(function () {
window.open('doc.php?q=' + this.id, 'D' + this.id,
'menubar=0,toolbar=0,directories=0,location=0,status=0,' +
'resizable=0,scrollbars=0,width=600,height=300');
});
//$('#' + id).click().id = row.cells[4];
}
}
return data;
}
$(document).ready(function() {
preProcess(document.getElementById('myTable'));
});
</script>
</body>
</html>
My corrections were the following (I think some might be due to transcription when you were copying the code for the post):
I replaced cell with cells
I added innerHTML after the cell index
I set the link to javascript:void instead of javascript.void
I commented out the line $('#' + id).click().id = row.cells[4];, because I had no idea what it did.
With those changes it worked like a charm.
I hope this helps.

Related

Hide/show function jquery or javascript calling more than one action, but not at the same time

So i want to make a remove player button, so that everytime i click it, it hides a images/button, the same for a add player function.
function addplayer(){
for (var i = 51; i <= 94; i++) {
if (i == 51 || i == 61 ||){
$("#" + "addplayer" + i).show();
}
}
}
This is my html caller
<button onclick="addplayer();"style="bottom:55;position:fixed;right:10;width:100;height:40px;background-color:#FFFFFF;">addplayer</button>
document.getElementById("addplayer2").onclick=function(){
document.getElementById("51Container").style.display="inline-block";
document.getElementById("52Container").style.display="inline-block";
document.getElementById("53Container").style.display="inline-block";
document.getElementById("54Container").style.display="inline-block";
}
document.getElementById("addplayer3").onclick=function(){
document.getElementById("61Container").style.display="inline-block";
document.getElementById("62Container").style.display="inline-block";
document.getElementById("63Container").style.display="inline-block";
document.getElementById("64Container").style.display="inline-block";
}
(i got 6 in total completly looking the same), just to illustrate, how it would work
Theese are my add player function, just on 5 different buttons, just to showcase that it is doing something, it doest seem to work for me, how do i do this, so that the same button will add (show), different object instead of the solution i got atm.
Hope somebody will help me.
If you want to invoke the function which is assigned to the addplayer# control, instead of calling $("#addplayer" + i).show() try calling $("#addplayer" + i).click() .. however, based on our back-and-forth, it seems that your i needs some attention.
As you said, your addplayer# controls are buttons, therefore, I suggest the following:
function addplayer(){
$("button[id^='addplayer']").each(function(i,e) { $(e).click(); });
}
This will invoke any click event function defined for any buttons whose id starts with addplayer.
See this fiddle for an example of how it works: https://jsfiddle.net/qhevn1x3/2/
Although I do not know your page's exact makeup, I would suggest something along these lines (if possible):
<div id='51Container'>some player</div>
<button class="addPlayer" data-id="51">Add Player</button>
<button class="removePlayer" data-id="51">Remove Player</button>
Then my JS would be something like:
// Page-level execution to assign my click events
$(function() {
$("button.addPlayer").on("click", function() {
var id = $(this).attr("data-id");
$("#" + id + "Container").css({'display':'inline-block'});
$("button.addPlayer[data-id='" + id + "']").toggle();
$("button.removePlayer[data-id='" + id + "']").toggle();
});
$("button.removePlayer").on("click", function() {
var id = $(this).attr("data-id");
$("#" + id + "Container").css({'display':'none'});
$("button.addPlayer[data-id='" + id + "']").toggle();
$("button.removePlayer[data-id='" + id + "']").toggle();
});
})();
This will wire up the add/remove buttons with the corresponding player. Then, if you wish to have a button to hide/show multiple, you need only select the appropriate add/remove buttons (by filtering by data-id) and invoke their click events.

Sending a Static Value as a Parameter in an Event Listener

Using Javascript (and jQuery), I have an array of several objects. For each object in that array, I'm adding a small, clickable image to a div. Clicking each small image will change the path of a large image, thus showing a new picture.
Here's what I've got:
for (s = 0; s < splash.length; s += 1)
{
// add a new dot to this row
$(".splash_dots").append ("<div class = 'dot' id = 'dot" + s + "'><img src = 'images/dot2.png'></div>");
var id = 'dot' + s;
// add a click handler
document.getElementById (id).addEventListener ("click", function () {change_splash (s)});
}
//////////////////////////////////////////////////
function change_splash (s)
{
// load a new image for the large photo
$(".splash").attr ("src", splash[s].screenshot_link);
}
This almost works. The problem is that when change_splash() is called, I'm expecting the value of s to be what it was when the loop added the handler. For example, clicking the first dot will call change_splash(0). However, it's actually sending what s is when the click happens (after the loops ends, which is always array.length).
I have a temporary solution, but I don't like it::
if (s === 0) document.getElementById (id).addEventListener ("click", function () {change_splash (0)});
if (s === 1) document.getElementById (id).addEventListener ("click", function () {change_splash (1)});
This just seems really inefficient.
I'm basically just trying to create a sliding splash advertisement, like what you'd see on almost any retail homepage, so I know it's possible. Any ideas are appreciated.
Try with this
for (var s in splash){
// create the points, stores the id in the data-id attribute
$(".splash_dots").append ("<div class ='dot' data-id='" + s + "'><img src='images/dot2.png'></div>");
}
// click listener
$("body").on("click",".dot",function(){
// retrieve the id from the data-id attribute
var s = $(this).attr("data-id");
$(".splash").attr ("src", splash[s].screenshot_link);
});
See it in action in JSFiddle demo
you'll need a closure:
(function(id,s){
document.getElementById(id)
.addEventListener("click", function(){change_splash (s)});}(id,s));

jQuery id does not yield text representation

I’d like to add a button to certain text fields to allow for additional input methods. Since the button should be able to reference the text field it belongs to, I'm adding a parameter to the function call within the button’s onClick() handler, containing the ID of the text field.
At least, this is my plan. When I obtain the ID of the text field, and display it in an alert, it displays nicely. However, when I use the result of $(this).attr('id') as a function parameter, I'd expect a string to be given to the function (the id of the element). Instead some weird object is given.
How do I convert that object to a string? Or is there a conceptual flaw?
<form>
<input class="joeDateTime" type="text" name="n1" id="n1" value="2014-09-01 17:30:00">
</form>
<script>
function handleJoeDateTime(e)
{
alert('Edit '+e); // shows 'Edit [object HTMLInputElement]'
}
$(document).ready(function() {
$('.joeDateTime').each(function(){
var i = $(this).attr('id');
alert(i); // shows 'n1'
$('<button onclick="handleJoeDateTime(' + i + ');return false;">πŸ“…</button>').insertAfter($(this));
});
});
</script>
You are not passing i as a string value, you are passing it as an variable. In modern browsers the element's id are copied to properties of the window object(so you can access then as global variables).
So you need to enclose them using quotes to pass i as a string value
$('<button onclick="handleJoeDateTime(\'' + i + '\');return false;">πŸ“…</button>').insertAfter($(this));
Demo: Fiddle
Also Instead of using inlined event handlers, I would recommend using jQuery event handlres
$('.joeDateTime').each(function () {
var i = $(this).attr('id');
console.log(i); // shows 'n1'
$('<button />', {
text: 'πŸ“…',
click: function () {
handleJoeDateTime(i);
return false;
}
}).insertAfter(this);
});
Demo: Fiddle
Your problem lies here:
$('<button onclick="handleJoeDateTime(' + i + ');return false;">πŸ“…</button>')
where this should be
$('<button onclick=\"handleJoeDateTime(\"' + i + '\");return false;\">πŸ“…</button>')
When you're passing an element to jQuery ( $ ), it becomes a jquery object.
It had been made to handle id, class, elements, not html chunks.
What you want is inserting a piece of concatenated elements as an html node.
so first concatenate your elements then append it with the jQuery's after() method.
(or create/append it with vanilia js var btn = document.createElement("BUTTON");)
var Button = '<button class=\"AltBut\" id=\"' + i + '\">πŸ“…</button>';
$(this).after(Button);
or ( for compacity )
$(this).after('<button class=\"AltBut\" id=\"' + i + '\">πŸ“…</button>');
In this exemple, I'm adding an id to each enabled buttons where I store your variable i
Then add a click listener to those buttons, avoid inline js at all price, for maintainability's sacke.
$('.AltBut').on('click',function(){
var i = $(this).attr("id");
alert("i= "+i);
return false;
})
The whole demo is here: http://jsfiddle.net/x6x4v90y/1/

How can I remove only one attribute value and not another with jQuery?

So I have found all over stackoverflow how to remove an entire attribute, however I only want to remove the first value of an onClick attribute but not the second. All my different instances of each container have a unique function associated to them and they all share the first function in common... Once any of these containers are clicked, the first function needs to be gone but I need to retain the second without altering it at all. My code follows:
<div class="halves marginal" onClick="buildFrame(),viscalc()">
<div class="two_one marginal" onClick="buildFrame(),percentOf()">
etc etc
Once buildFrame() executes once:
function buildFrame(){
document.getElementById('screenframe').innerHTML = "<img src='img/screen.png'><iframe id='framecontent'>";
I would like to remove it from each class ( but keep percentOf(), viscalc() etc etc )
How can I remove only one attribute value and not the other?
so this is what i would do.
$("div.marginal").on("click", function(){
$(this).off("click", buildFrame);
});
that removes it from the event queue for the functions listener.
You cant do it with anon functions.
idea:
$("div.marginal").on("click", function(){
var funcs = $(this).attr("onclick").split(",");
$(this).attr("onclick", funcs[1]);
console.log("value of 'onclick'", $(this).attr("onclick"));
// or alternatively
$(this).off("click", funcs[0].substr(0,funcs[0].length-2))
//this breaks apart the onclick, turns off the click event for event0, while maintaining
// the second event.
});
the issue that i can see, from my point of view is that since it is inline, it really executes it once, so changing onclicks value might not be enough as it might not be refreshed. The above jquery though, will do what your case would want.
I think the most pin-pointed approach to this would be pass this in your buildFrame and then inside the buildFrame remove itself from the calling object.
<div class="halves marginal" onClick="buildFrame(this),viscalc()">
function buildFrame(obj){
document.getElementById('screenframe').innerHTML = "<img src='img/screen.png'><iframe id='framecontent'>";
// not sure why your not closing the iframe tag ill assume you wanted it this way
$( this ).off("click", buildFrame);
};
** EDIT **
Missed your edit where you want to remove it from all instances, this complicates it much, this solution is no longer viable.
Fairly straightforward to do with a custom selector. I think this can be done without the eval() but I'm not sure how offhand.
Here you go:
HTML
<div class="halves marginal" onClick="buildFrame();viscalc();"></div>
<div class="two_one marginal" onClick="buildFrame();percentOf();"></div>
Javascript
$.expr[':'].hasFrameMethod = function(obj){
var onClick = $(obj).attr('onClick');
return onClick != undefined ? onClick.split(';').indexOf("buildFrame()") != -1 : false;
};
function buildFrame(){
document.getElementById('screenframe').innerHTML = "<img src='img/screen.png'><iframe id='framecontent'>";
$('div:hasFrameMethod').each(function(idx, item){
htItem = $(item);
var itemClickFns = htItem.attr('onClick') != undefined ? htItem.attr('onClick').split(';') : (htItem.attr('onclick') != undefined ? htItem.attr('onclick').split(';') : "");
htItem.attr('onClick', '');
htItem.attr('onclick', '');
htItem.off("click");
// re-add any correct secondary actions
if(itemClickFns.length > 1){
var strFnClick = "function onclick(event) { ";
itemClickFns.forEach(function(elem,idx){
if(elem != "buildFrame()"){
if(elem.length > 0){
strFnClick += elem + ";";
htItem.attr('onclick', htItem.attr('onclick') + elem + ";");
}
}
});
strFnClick += "}";
// Rebind the event
htItem.prop('onclick', eval(strFnClick));
htItem.click(eval(strFnClick));
}
});
}
As long as this code loads after jQuery it should work. :)

jQuery does not see my dynamically loaded checkboxes

I have written a script that will load a series of products into a div tag.
I now want to be able to filter those products using a series of checkboxes.
jquery makes a $post to an ASP page that returns an XML dataset. The first element of the data set contains a list of manufacturers in this format ara|dai|sid|alp etc. The second element contains the manufacturer names of the codes above.
I then use this script to build a list of checkboxes into a div tag.
var mc = new Array();
mc = $("manCodes",xml).text().split(",");
var manTitles = new Array();
manTitles = $("manTitles",xml).text().split(",");
for ( var i=0, len=mc.length; i<len; ++i ){
m += '<span><input type="checkbox" value="' + mc[i] + '" name="man[]" id="man_'+i+'" />' + manTitles[i] +'</span>'//mc[i];
}
manufacturers = '<div id="filter" class="man">FILTER<br />' + m + '</div>';
$(".formSelect").append(manufacturers);
This works a treat and then in the Document Ready section I have a code segment that looks for any click on a checkbox:
$(document).ready( function() {
$("input:checkbox").click(function() {
loadXML($("#sortOrder option:selected").val(),$("#limitBy option:selected").val(),$("#productGroup").val());
});
});
This is where my code falls over because any click on any checkbox is not working. Its almost like JQuery cannot see these checkboxes that it has created.
Can anybody please help how to resolve this problem please?
Cheers
Graham
You need event delegation, otherwise the event handler will not fire for dynamically inserted/removed elements. .live or .delegate are two options.
$("input:checkbox").live("click", function() {
// implementation
});
or better:
$("form").delegate("input:checkbox", "click", function() {
// implementation
});
use jQuery.live() to capture events from dynamically added elements.
Attach a handler to the event for all
elements which match the current
selector, now and in the future.
$("input:checkbox").live("click", function() {
loadXML($("#sortOrder option:selected").val(),$("#limitBy option:selected").val(),$("#productGroup").val());
});
Try:
$(document).ready( function() {
$("input:checkbox").live("click",function() {
loadXML($("#sortOrder option:selected").val(),$("#limitBy option:selected").val(),$("#productGroup").val());
});
});

Categories

Resources