I need to pass extra arguments to onclick handler. I can't decide which way is "better":
EDIT:
Context: I have a table that shows roster of an event. Each row has a 'delete' button. What is a better way to pass recordId to the delete-handler?
$('a.button').click(function() {
var recordId = $(this).metadata().recordId;
console.log(recordId);
});
...
<tr>...<a class='{recordId:1} button'>delete</a></tr>
<tr>...<a class='{recordId:2} button'>delete</a></tr>
or
function delete(recordId) {
console.log(recordId);
}
...
<tr>....<a class='button' onclick='deleteRecord(1)'>Delete</a></tr>
<tr>....<a class='button' onclick='deleteRecord(2)'>Delete</a></tr>
What are the pros and cons for each option?
NOTE: I use a.button as a custom, CSS-styled button, it does not behave as a link.
EDIT:
I would appreciate alternative solutions as well, if you can argument the advantages of offered alternatives.
Store the record id as an attribute of element itself, but instead of using the metadata plugin which stores it in a weird format, I would recommend you use HTML5's data attributes that is also backwards compatible.
A row would look like:
<tr> .. <a data-id="1">delete</a> .. </tr>
In the handler, retrieve the attribute value and act on it
function deleteRecord() {
var rowId = $(this).attr('data-id');
...
}
It is comparable to using the metadata plugin, but it does not overload the class attribute. No extra plugins are needed for this. It uses a single handler just as the metadata plugin does which is performant for large datasets.
The inline onclick handlers are bad for the same reasons. A new handler is created per row. It cuts down on flexibility and is generally a bad practice.
I would just go with your second approach - it's the simplest and there is nothing wrong with it.
$('a.button').click(function() {
var classes = $(this).attr('class').split(' ');
var option;
for( var i in classes )
{
if( classes[i].indexOf( 'option' ) != -1 )
{
option = classes[i].substr( 6 );
break;
}
}
console.log( option );
});
...
<a class='option-yes button'>Yes</a>
<a class='option-no button'>No</a>
Edit:
It sounds like you're assigning data to these 'buttons' dynamically. You'd be better off assigning the data to the button using jQuery's .data() method and then getting the data from there. I have updated my example code.
If each type of button performs a different action then use a different handler for each type of button:
$('a.button').click(function (e) {
// Stuff with $(this).data().recordId
});
$('a.button.no').click(function (e) {
//Stuff
});
...
<a class="button yes">Yes</a>
<a class="button no">No</a>
Related
I'm mapping currencies from a json file and i render the mapped currencies to a component. I have a .php file like this
<div class="currency-switch-container" id="currency_container">
<span style="font-size:12px;font-weight:bold">All currencies</span>
<div id="currency-map" style="margin-top:15px"></div>
</div>
I refer the div in the above component in my js file as follows
let currencyMap = jQuery("#currency-map");
And when my jQuery document is ready i'm doing the following
jQuery(document).ready(function($) {
$.getJSON('wp-content/themes/mundana/currency/currency.json', function(data) {
for(let c in data){
currencyMap.append(`<span onclick="onCurrencyClick(${data[c].abbreviation})"
class="currency-item">
<span>
${data[c].symbol}
</span>
<span>
${data[c].currency}
</span>
</span>`)
}
});
}
and my function is like this
function onCurrencyClick(val){
console.log("val",val);
setCookie("booking_currency", val, 14);
}
Here the function does not work. But if i do not pass anything to the function it seems to work as i can see the log in the terminal.
Hi your expression ${data[c].abbreviation} will put the value into function string without string quotes i.e. the resultant would be onCurrencyClick(abbreviation) while it should be onCurrencyClick('abbreviation').
please use onclick="onCurrencyClick('${data[c].abbreviation}')" instead.
Instead of using the inline onclick, use event delegation. This means that you have a single event listener that handles all the events from the children and grandchildren. The modification is a very minor one seeing the example here below.
A reason for doing this is that you keep your JavaScript inside your JS file. Like now, you encounter a JS error and have to look for it in your HTML. That can get very confusing. Also however inline onclick listeners are valid, they are outdated and should be avoided unless there is absolutely no other way. Compare it with using !important in CSS, same goes for that.
function onCurrencyClick(event){
var val = $(this).val();
setCookie("booking_currency", val, 14);
}
currencyMap.on('click', '.currency-item', onCurrencyClick);
This example takes the val that you try to insert from the value attribute from the clicked .current-item. <span> elements don't have such an attribute, but a <button> does and is a much more suitable element for it expects to be interacted with. It is generally a good practice to use clickable elements for purposes such as clicking.
In the example below you see the button being used and the abbreviation value being output in the value attribute of the <button> element and can be read from the onCurrencyClick function.
jQuery(document).ready(function($) {
$.getJSON('wp-content/themes/mundana/currency/currency.json', function(data) {
for(let c in data){
currencyMap.append(`
<button value="${data[c].abbreviation}" class="currency-item">
<span>
${data[c].symbol}
</span>
<span>
${data[c].currency}
</span>
</button>
`)
}
});
onclick will not work for a dynamically added div tag
Yo should follow jQuery on event
Refer: jQuery on
Stackoverflow Refer: Dynamic HTML Elements
I can't figure this out. I'm trying to create an onclick handler purely in Javascript.
What I plan to do here is inside this DIV, have a collection of items that I can click on. For now, these items will be numbers from 0 to 9 inclusive. When a number is clicked on, a system message consisting solely of that number should pop-up on the screen. I narrowed my problem down to just the onclick handler definition.
If I use this format:
item[n].onclick=function(n){
handler(n);
}
The handler will fire only when click a number which is correct, but the message that appears is something about mouse event.
If I use this format:
item[n].onclick=function(){
handler(n);
}
The handler will pass a value of -1 which in turn is printed as a message. I think it means "false".
How do I modify this:
item[n].onclick=function(){
handler(n);
}
so that 'n' being used as the handler parameter is the same as the number I click on the screen?
My code is the following:
<div ID="Itemset"></div>
function handler(n){
alert(n);
}
collections=document.getElementById('Itemset');
for(n=0;n<10;n++){
item[n]=document.createElement('DIV');
item[n].innerHTML=n;
collections.appendChild(item[n]);
item[n].onclick=function(n){
handler(n);
}
}
What I'm effectively trying to do if you want to understand it HTML wise is this:
<div ID="Itemset">
<div onclick="handler(0);">0</div>
<div onclick="handler(1);">1</div>
<div onclick="handler(2);">2</div>
<div onclick="handler(3);">3</div>
<div onclick="handler(4);">4</div>
<div onclick="handler(5);">5</div>
<div onclick="handler(6);">6</div>
<div onclick="handler(7);">7</div>
<div onclick="handler(8);">8</div>
<div onclick="handler(9);">9</div>
</div>
Except that I don't want to write out onclick="handler(n);" a million times.
Any advice? and feel free to point to another resource that has the answer I need if there is one.
UPDATE
I'm looking for something compatible with older browsers as well. I'm going to have to not go for the bind function because according to mozilla docs, it works for IE 9+. I'm looking for something that works for IE 7+ as well as other browsers. I might have to go for event listeners if there is no other alternative.
You have a closure issue here (see JavaScript closure inside loops – simple practical example), a simple solution is to use bind to use the current value of n to be a parameter of the handler function
item[n].onclick=handler.bind(item[n],n);
U can use addEventListener and ID for find clicked element...
document.getElementById("Itemset").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a list item
var value_data = parseInt(e.target.textContent);
if(e.target && value_data > -1) {
alert("Malai test:: "+value_data);
//handler(value_data);
}
});
https://jsfiddle.net/malai/tydfx0az/
I found my answer here: https://bytes.com/topic/javascript/answers/652914-how-pass-parameter-using-dom-onclick-function-event
Instead of:
item[n].onclick=function(n){
handler(n);
}
I have to do:
item[n].onclick=new Function('handler('+n+')');
Funny thing is, the word function needs to be capitalized when making a new instance. It's awkward I have to go this route but it works in IE 7+
One alternative is :
function handler(){
alert(this.id);
}
function myFunction() {
var item=[];
collections=document.getElementById('Itemset');
for(n=0;n<10;n++){
item[n]=document.createElement('DIV');
item[n].innerHTML=n;
item[n].setAttribute("id","itemset"+n);
collections.appendChild(item[n]);
item[n].onclick=handler;
}
}
Insert dynamic ids to the elements and when you click on any element retrieve its id using this.id and do whatever you want to do with that value.
That's all.
Hope this helps.
onclick="triggersTracking($(this).attr('a'),$(this).attr('b'),$(this).attr('c'),Enum.BtnSellerView)"
I have this line at various HTML tags/buttons. I want to move this code to one place for better maintainability. The problem is with third/last attribute i am passing since its Enum, it has different values being passed from different tag elements.
How can i move it to one common place where it would get invoke. For example I could have made a class if i have had just these (this).attr since its common for every tag.
You can do like
Give all the elements a common class name
Then add a data attribute, "data-enum" to each tag with corresponding value.
Then you can write the code like this,
$(".className").click(function () {
var a = $(this).attr('a');
var b = $(this).attr('b');
var c = $(this).attr('c');
var enum = $(this).data('enum');
});
You can use jquery to get this:
$("body").on("click", "someClass", function() {
//code here
});
you don't need to write $(this).attr('a'),$(this).attr('b'),$(this).attr('c')
again and agin on every onclick just paas this object and get them all in function like :
onclick="triggersTracking(this,Enum.BtnSellerView)"
function triggersTracking(obj,enumVal){
// get these values here by obj (no repetitive code needed in every onclick )
$(obj).attr('a')
$(obj).attr('c')
$(obj).attr('b')
}
Do some thing like this
.click()
.on()
.data()
if you use attributes like data-enum , data-e....
so use $(this).data() it will return all attributes in JSON which is starting
from data-
$('.click').click(function(e) {
console.log($(this).data())
$('body').append($(this).attr('a'))
})
// if you have dynamic html tag then go for .on
$('body').on('click','.click',function(){
//callback
console.log($(this).data())
$('body').append($(this).attr('a'))
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="click" a="i am a attr of p" b="i am b" data-a="i am data a">i am p</p>
<h2 class="click" a="i am a attr of h2">i am h2</h2>
I've try use jsrender/jsviews first time. It looks awesome, but I'm not find clear documentation or example how to dynamically bind event handlers for generated content.
For example pure jQuery old approach was:
Code from bean class to render collection of objects:
container = $('#tabs-my');
this.load( // Obtain array of objects
$.proxy(function(list){
container.html('');
list.forEach(
$.proxy(function(it, i){
container.append(this.renderItem(it));
}
,this)
);
}
,this)
);
And in object itself render method:
,renderItem: function(/*Specialist*/ it){
var container = $('<div class="specialist-item" />')
container.append(
$('<span class="x">Remove</span>').click($.proxy(function(){
this.removeSpecialistFromList(it.id)
}
,this))
);
container.append(
$('<span class="x">Edit</span>').click($.proxy(function(){
this.renderSaveForm(container)
}
,this))
);
container.append('<p><b>' + it.name + '</b> <i>' + it.phone + '</i></p>' +
'<p>' + it.skill + '</p>' +
( it.city ? '<p>' + it.city.name + '</p>' : ''));
return container;
}
Note I bind handler via closure content for current object without use any external identificators in tag itself.
Then I try use templating to separate content from visialisation.
My template:
<div class="specialists-list">
Items in list: {{:specialists.length}}
{^{for specialists}}
<div class="specialist-item">
<span class="x">Remove</span><span class="x">Edit</span>
<p><b class="name">{{:name}}</b> <i class="phone">{{:phone}}</i></p>
<p class="skill">{{:skill}}</p>
<p class="city">{{:city.name}}</p>
</div>
{{/for}}
</div>
And rendered like:
var template = $.templates({
specialistsTmpl: tmpl
});
$.templates.specialistsTmpl.link(container, {
specialists: list
});
I realize it could be done using common handlers in attributes something like:
<span class="x" data-id="{{:id}}">Edit</span>
And then try obtain that object again from external. But it is workaround and is not desired.
Is there way to bind handlers in template or via helpers, custom tags?
There are many samples on http://www.jsviews.com which include event binding, and handlers such as for removing or inserting data items. Did you look at the examples here: http://www.jsviews.com/#samples/editable - you will find four different approaches to the same scenario.
For example:
Template
{^{for languages}}
<input data-link="name" />
<img class="removeLanguage" .../>
{{/for}}
Code:
$.link.movieTmpl("#movieList", app)
.on("click", ".removeLanguage", function() {
var view = $.view(this);
$.observable(view.parent.data).remove(view.index);
return false;
});
Note the use of var view $.view(this); - you pass in the element (this) that is clicked on, and $.view(clickedElement) returns you the view, from which you can get view.index (the item index - in the case of iteration over an array), view.data (the current data item - in you case that would be the specialist item), view.parent.data (in your case, the specialists array) etc.
Of course since view.data is the current data item, if your data item is in effect a view model, with methods, you can call a method: view.data.someMethod(...).
But as an alternative to using the jQuery on() for binding handlers, you can use declarative binding directly in the template like this:
{^{for specialists}}
<div class="specialist-item">
<span class="x" data-link="{on removeMe}">Remove</span> ...
...
</div>
{{/for}}
where I assume your specialist has a removeMe() method. The "{on ...}" binding binds by default to the click event, and you can bind to methods on your data, or to helpers, etc.
Take a look at this example: http://jsfiddle.net/BorisMoore/cGZZP/ - which uses {on ...} for binding to helpers for modifying a two-dimensional array.
I hope to create some new samples using {on ...} binding before too long.
BTW I don't recommend using the onAfterCreate for doing event binding. Either of the above approaches are better, and will ensure correctly disposal of event bindings.
I'm not 100% sure I completely understand but I believe I do. You want to use a different approach. Look at .on for jQuery. I've switched to using it most all the time. The call back function doesn't need to change. It is really pretty nice.
In my case, I was creating thousands of event handlers and it was killing my performance. So I switched to using .on and it solved my problem.
This doesn't exactly answer your question... but I think its a better solution.
My code is meant to replace radio buttons with dynamic ones, and allow clicking both the label and new dynamic radio element to toggle the state of the hidden with CSS radio box.
I need to send to questions.checkAnswer() three parameters, and these are defined within these initiation loops. However I always get last the last values once the loop has finished iterating. In the past I've created dummy elements and other things that didn't feel right to store 'temporary' valuables to act as an informational hook for Javascript.
Here is what I have so far
init: function() {
// set up handlers
moduleIndex = $('input[name=module]').val();
$('#questions-form ul').each(function() {
questionIndex = $('fieldset').index($(this).parents('fieldset'));
$('li', this).each(function() {
answerIndex = $('li', $(this).parent()).index(this);
prettyRadio = $('<span class="pretty-radio">' + (answerIndex + 1) + '</span>');
radio = $('input[type=radio]', this);
radio.after(prettyRadio);
$(radio).bind('change', function() {
$('.pretty-radio', $(this).parent().parent()).removeClass('selected');
$(this).next('.pretty-radio').addClass('selected');
questions.checkAnswer(moduleIndex, questionIndex, answerIndex);
});
prettyRadio.bind('click', function() {
$('.pretty-radio', $(this).parent().parent()).removeClass('selected');
$(this).addClass('selected').prev('input').attr({checked: true});
});
$('label', this).bind('click', function() {
$(radio).trigger('change');
questions.checkAnswer(moduleIndex, questionIndex, answerIndex);
$(this).prev('input').attr({checked: true});
});
});
});
Is it bad to add a pretend attribute with Javascript, example, <li module="1" question="0" answer="6">
Should I store information in the rel attribute and concatenate it with an hyphen for example, and explode it when I need it?
How have you solved this problem?
I am open to any ideas to make my Javascript code better.
Thank you all for your time.
It's not the end of the world to add a custom attribute. In fact, in many cases, it's the least bad approach. However, if I had to do this, I would prefix the attribute the with "data-" just so that it is compliant with HTML5 specs for custom attributes for forward compatibility. This way, you won't have to worry about upgrading when you want to get HTML5 compliant.
you need to say 'var questionIndex' etc, else your 'variables' are properties of the window and have global scope...
regarding custom attributes, i have certainly done that in the past tho i try to avoid it if i can. some CMS and theming systems occasionally get unhappy if you do this with interactive elements like textareas and input tags and might just strip them out.
finally $(a,b) is the same as $(b).find(a) .. some people prefer the second form because it is more explicit in what you are doing.
If the assignment of the custom attributes is entirely client-side, you must resolve this with jQuery data, something like this:
$("#yourLiID").data({ module:1, question:0, answer:6 });
for the full documentation see here