Attribute Contains Selector * alternative - javascript

NOTE: using jQuery 1.3.2 (Yep I'm upgrading but still need to be at 1.3.2 for this)
Looking for a better way to implement this
HTML Element (This is coming from a custom PHP framework and is very cumbersome to make changes to):
<select id="car[0][car_id]" name="car[0][car_id]">
... 100+ options
</select>
I can add another drop down dynamically which looks like this:
<select id="car[1][car_id]" name="car[1][car_id]">
... 100+ options
</select>
and
<select id="car[2][car_id]" name="car[2][car_id]">
... 100+ options
</select>
Now I'm iterating through them like this:
function showCarinfo() {
for ( var car_number = 0; $("#car\\["+car_number+"\\]\\[car_id\\]").length > 0; car_number++ ) {
// do stuff
}
}
on change selection:
$("input[name*=car_id]").change(function(){
// display info for each car selected from each drop down select menu
showCarinfo();
});
Is there a better way to implement the on change and iterating methods? I am using the 'Attribute Contains Selector' ( http://api.jquery.com/attribute-contains-selector/ ) but looks like it's causing a big performance issue with the on change event.
UPDATE:
I've replaced the * with $ to search the last part and this (I think) does help with performance a little but still looking if this is the best solution
$("input[name$=car_id]").change(function(){
// display info for each car selected from each drop down select menu
showCarinfo();
});

The performance issue is in your showCarinfo function. You're preforming DOM selection in a loop.
Don't do that. Instead cache the selection...
function showCarinfo() {
var cars = $("input[name*=car_id]");
for ( var car_number = 0; car_number < cars.length; car_number++ ) {
cars.eq(i); // do stuff
}
}
or you can use .each()
function showCarinfo() {
$("input[name*=car_id]").each(function() {
// do stuff
});
}
Also, if the only thing the handler is doing is calling that function, a small optimization would be to do this...
$("input[name*=car_id]").change(showCarinfo);

Just as a guess, but I would try binding the change event to all input elements and filtering them when the event is called:
$("input").change(function() {
if ($(this).attr('name').indexOf('car_id') == 0) {
showCarinfo();
}
});
That way jQuery doesn't have to block page execution to attach event handlers to the elements based on selectors.

You can use this:
$('input[name$="[car_id]"]').change(function(){
// codes
});

Related

How to get td values from dynamically created table

I see a lot of similar questions but not one that directly targets my problem. The business logic of my problem is that I allow the user to open a jQuery Dialog where I create table loaded with a data from a database and when the user make a choise I load the selected data info fields from the main screen.
My current problem is with collecting the data from the <tr> which happens on button click. If it was a hard coded table I would just:
$(selector).on('click', function(){
var $item = $(this).closest("tr").find('td');
})
and then do something with $item however the table is created dynamically (from Ajax request) everytime the Ajax request is made the table is destroyed and recreated so basically I can't or at least I don't know a way to use some sort of selector to which to bind the event so I can reproduce the above code.
Instead in the dynamic table I have this:
<td><button onclick="getData();return false">Select</button>
The problems with this (at least how I see it) are two - first, the using of onclick inside HTML element. From what I know it's not a good practice and there are better alternatives and I would appreciate answer showing this. Also, even though I go with this code I'm yet unable to extract the text from each <td> in:
function getData() {
...
}
I tried several approaches including the one which was working with the static table and the binded event handler.
At the end here is a JS Fiddle example where I think I made it clear what I can and what I can not do, so you can refer to it.
Check this fiddle
$(selector).on('click', function(){
var $item = $(this).closest("tr").find('td');
})
Using the above code you are binding a direct event but the one which you want is delegated event
To use delegated event you should use like
$(document).on('click',selector, function(){
var $item = $(this).closest("tr").find('td');
})
so your final code will look something like
$(document).on('click','.get-data' ,function(){
var $item = $(this).closest("tr").find('td');
$.each($item, function(key, value){
alert($(value).text());
})
});
document can be anything which is parent to the table which is going to be created.
Dont forget to add the selector while adding a new table element
I had the same problem and solved it that way.
You can create your table with the database results like this:
for (var i = 0; i < results.length; i++) {
// create table row and append it to the table using JQuery
// next create a td element, append it to the created tr
// and attach a listener to it
$('<td/>').html(results[i].textProperty)
.appendTo($(tr))
.on('click', getData);
}
where getData() is your function.
You can pass arguments to your getData like this:
.on('click', {info: results[i].data}, getData);
Then you can access them in your function:
function getData(event) {
console.log(event.data.info);
}
Hope this helps!
Edit: This way you are creating a listener for each td. An optimization could be to create a listener for the whole class of td elements and to pass data to it via HTML attributes or text value like in the approved answer.
or you can use this pass object in getdata method
$('#build-table').on('click', function(){
$('#temp-table').append('<table><thead><tr><th>Select</th><th>Name</th> </tr></thead>' +
'<tbody><tr><td><button class onclick="getData(this);return false">Select</button></td><td>Name1</td></tr>' +
'<tbody><tr><td><button onclick="getData(this);return false">Select</button></td><td>Name2</td></tr>' +
'</tbody></table>')
});
function getData(ob) {
var $item = $(ob).closest("tr").find('td');
$.each($item, function(key, value){
alert($(value).text());
})
}

Find Data in jquery

I'm currently working on my Stretchbox Plugin and trying to optimize and shorten the code.
I've used the jquery data method to attach data to certain divs.
For instance:
$('#thumbs div:last-child').data('active', true)
which sets a certain div to the active state.
If i know want to find this div, i have to check each .thumb class
in order to find it:
$('.thumb').each(function() {
if($(this).data('active')){
//Do Stuff
}
}
This works fine, but I'm quite sure there should be a much easier way, since checking up every single .thumb div(out of 10-30) will take some performance too.
$(".thumb[data-active='true']");
As far as I know there is no other way to do it. You could, however, create a new jQuery selector. I was going to give it a shot, but it looks like someone has already thought of it (scroll down to "Querying element data").
It will allow you to do things like this:
$(':data(active)'); //Selects all elements with "active" data
It probably won't be faster, but it might make your code neater!
Am I missing something? Just save it in a variable:
jQuery(function($){
var activeDiv = [];
$('#selector').click(function(){
activeDiv = $('#thumbs div:last-child')
...
});
$('#executor').click(function() {
activeDiv.each(function() {
...
});
}
});

How to make a list in a web page that can be edited without reloading

Here's some low-hanging fruit for those more comfortable with Javascript than I...
I want to improve a Moodle plugin's admin UI. (Moodle is a PHP-based web app). What I need to do is take what is currently a text box, with semi-colon delimited entries and replace that with a editable list.
The HTML elements I would use is a select list, a text input field and another hidden textfield. I guess a couple of submit buttons too, one for adding, and the other for removing of entries.
The behaviour would be:
Entries can be added to the select list from the visible textbox upon some kind of submit (this cannot reload the page).
The hidden textbox would contain all the entries from the select list, just semi-colon delimited
There's a function to remove entries from the select list that also does not reload the page.
The hidden textbox is updated with add/remove actions
This seems to me like something that's easy enough. Though I'm having a hard time finding a close enough example to rip off.
This sample code is as close as I've found thus far. There's got to be some good examples of precisely this sort of thing out there. Any decent pointers will be rewarded with + votes.
What you want to do is use JavaScript and manipulate with the DOM of the webpage. Basically, the HTML of a webpage is parsed and rendered by the browser into a tree of elements. Each HTML tag like <select> is an element in the tree. You use JavaScript to interact with this tree by performing operations like removing elements from this tree or adding elements to this tree. (Note that preforming operations on the tree will not refresh the page.)
The standardized API to do these sorts of manipulation in JavaScript is known as the DOM. However, many people, myself included, think that this API is very clunky and not nearly expressive enough. Doing even trivial things require tons of lines of code. For this reason, many developers do not use the DOM directly instead using more powerful libraries, such as jQuery, to make their lives easier.
Below is an example of some HTML + JavaScript that I think mimics most of your requirements. Ideally for learning purposes, this would be written purely using the standard W3C DOM API, but since your problem is not that trivial, I resorted to using jQuery instead.
The HTML:
<select id="list" multiple="multiple"></select>
<input id="removeButton" type="button" value="Remove"></input>
<div>
<input id="optionAdder" type="text"></input>
<input id="addButton" type="button" value="Add"></input>
</div>
<br>
<input id="clearButton" type="button" value="Clear All"></input>
<div>Not So Hidden: <input id="hidden" type="text"></input></div>
The JavaScript:
// Uses jQuery to define an on document ready call back function
$(function(){
// The code in this function runs when the page is loaded
var options = []; // contains all the options
// add new option to drop-down
var addOption = function(optText) {
// Create new option element and add it to the <select> tag
$('<option></option>')
.attr('value', optText).text(optText)
.appendTo( $('#list') );
};
// writes the names of all the options in the "hidden" text box
var fillHidden = function() {
$('#hidden').val('');
var hiddenText = "";
for(var i=0; i< options.length; i++) {
if(hiddenText) {
hiddenText += "; ";
}
hiddenText += options[i];
}
$('#hidden').val(hiddenText);
}
// Bind the click event of the "Add" button to add an option on click
$('#addButton')
.click(function(){
var optText = $('#optionAdder').val();
if(optText) {
addOption(optText);
}
$('#optionAdder').val('');
options.push(optText);
fillHidden();
});
// Bind the click event of the "Remove" button to remove the selected options on click
$('#removeButton')
.click(function(){
$('#list option:selected').each(function(){
var optIndex = $.inArray($(this).val(), options);
if(optIndex > -1) {
options.splice(optIndex, 1);
$(this).remove();
}
fillHidden();
});
});
// Bind the click event of the "Clear" button to clear all options on click
$('#clearButton')
.click(function(){
$('#list').children().remove();
options = [];
fillHidden();
});
});
Here is a jsfiddle demonstrating the code

jQuery equivalent to [function].parameters from javascript

I've had this javascript function that I've used many many times over ...
function showHideObjects()
{
var args;
args = showHideObjects.arguments;
for(var i=0;i<args.length;i++)
{
if(document.getElementById(args[i]))
{
if(args[i+1] == 'flip')
{
if(document.getElementById(args[i]).style.display == '')
{ document.getElementById(args[i]).style.display = 'none';}
else
{ document.getElementById(args[i]).style.display = '';}
}
else
{
document.getElementById(args[i]).style.display = args[i+1];
}
}
i++;
}
}
Now I'm working with ASP.NET and need that same function but in jQuery but I can't find any information about dynamic parameters in jQuery. Is there a way to do this in jQuery?
To provide a little more background ... you could call the above code with a line like ... showHideObjects('div1','none') and it'd hide div1. Or you could call ... showHideObjects('div1','none','div2','','div3','flip') and it'd hide div1, show div2 and switch div3 from either hidden or shown.
jQuery is just JavaScript. Your code will work fine with jQuery.
It is purely a javascript feature. Jquery do not have any special feature for this.
As some have already said, jQuery is Javascript and your code will work just fine. BUT you can do the same thing with jQuery that you are now doing with your function. You are getting elements which you change to have either display none or nothing.
jQuery has Selectors, which you can use to select the elements from the DOM to which you want to do something to. You can forexample use selectors to select the element you want and then just cast .toggle(). It does the exactly same thing as your function but with just 1 line of code.
EDIT: Added an example:
If you have a div, which has a class hideNseek, you can use this to toggle it to show or hide:
$(".hideNseek").toggle();
$(".hideNseek") is selectors which selects all classes that have that name.
Like others have said, your function will work just fine. You don't NEED jQuery to do what you are doing. If you wanted to use jQuery you could do something like:
function showHideObjects() {
var args = arguments,
l=args.length;
for ( var i=0; i<l; i++ ) {
var elem = $( "#"+args[i] ),
type = args[i+1];
if ( elem.length ) {
if ( type == "flip" ) {
elem.toggle();
} else {
elem.css("display", type);
}
}
i++
}
}
You can't call different actions on a group in JQuery. However, you can perform the same action (hide, show, toggle) on a group of elements by using the appropriate selector to get the group. Here's a quick example that has buttons to toggle all the div elements and all the span elements on the page with the JQuery toggle function, along with a fiddle for testing:
HTML:
<div>div 1</div>
<span>span 1</span>
<div>div 2</div>
<span>span 1</span>
<input id="toggleSpans" type="button" value="toggle spans"/>
<input id="toggleDivs" type="button" value="toggle divs"/>
JavaScript:
$('#toggleDivs').click(function(){
$('div').toggle();
});
$('#toggleSpans').click(function(){
$('span').toggle();
});
If that is not good enough, you can easily group and call these methods in a custom function as you do in your old JavaScript function, or just keep your old method.
UPDATE:
Check out the different selectors you can use. In your specific case, you'd need to use ID Selector along with Multiple Selector, but you could make your function much more powerful if you allowed passing in any JQuery selector rather than just the ids. For example:
showHideObjects('#div1, <otherSelectorsToHide>','none',
'#div2, <otherSelectorsToShow>','',
'#div3, <otherSelectorsToToggle>','flip');

How to get form fields from a specific fieldset?

I'm writing a HTML form that's divided in fieldsets, and I need to get the form fields from a specific fiedset in a function.
Currently it's like this:
function conta(Fieldset){
var Inputs = Fieldset.getElementsByTagName("input");
var Selects = Fieldset.getElementsByTagName("select");
/* Doing the stuff I need to do in two iterations, one for each field type */
}
But who knows what the future may hold, and if the form gets some new field types (radios, checkboxes) this could become awful to mantain.
I know that form elements have the elements attribute that returns all the form fields and I was hoping I could use something like that.
(I know I still gotta discriminate the field type in a bunch of conditionals inside the iteration, but I think it would be faster and easier to keep. Unless it isn't and I should not be doing it)
#Ryan is on the right track if you want to use jQuery (and I would), but I'd suggest something more along the lines of:
$('fieldset#fieldset1 > input[type=text]').each( function() {
... do something for text inputs }
);
$('fieldset#fieldset1 > input[type=radio]').each( function() {
... do something for radios }
);
$('fieldset#fieldset1 > select').each( function() {
... do something for selects }
);
$('fieldset#fieldset1 > textarea').each( function() {
... do something for textareas }
);
As an improvement over if-then-else constructs.
Radio buttons and checkboxes are still input tags and will be in the Inputs var. The problem is, you'll need to add handlers for the checked state to see which radio buttons and checkboxes are selected.
Even worse, you can have more than one radio button and checkbox with the same name... in fact you have to for radio buttons or they don't work as expected.
No jQuery, no problem:
function condat(fieldset) {
var tagNames = ['input', 'select', 'textarea']; // Insert other tag names here
var elements = [];
for (var i in tagNames)
elements.concat(fieldset.getElementsByTagName(tagNames[i]);
for (var i in elements) {
// Do what you want
}
}
Filip Dupanović solution together with the second Cargowire comment worked for me, but only with a minor modification. Cargowire's second comment only produced an array which just holds the sliced characters of the tagNames array (I would have written this in a comment, but I lack the rep so far).
Here is what worked:
function condat(fieldset) {
var tagNames = ['input', 'select', 'textarea']; // Insert other tag names here
var elements = [];
for (var i in tagNames) {
elements = elements.concat([].slice.call(fieldset.getElementsByTagName(tagNames[i])));
}
for (var i in elements) {
// Do what you want.
// Attributes of the selected tag's can be referenced for example as
// elements[i].value = ...;
}
}
A usefull application of this would be to define buttons which only reset a fieldset instead of the whole form. Just use elements[i].value = elements[i].defaultValue; in the //do what you want part, for text inputs to be reseted. And of course bind the condat function onto the onclick event of the button providing the fieldset dom element as a paramenter.
Haven't tested this and don't know how it would work, but you could use JQuery here to select all the elements into a JQuery object
//$("input select textarea").each(function() {
$(":input").each(function() { //even better
// do stuff here
});
this would at least cleanup the code, although you would still have to add conditional statements based on field type like you mentioned.
You should use just querySelectorAll:
function condat(fieldset) {
var elements = fieldset.querySelectorAll('input, select, textarea');
elements.forEach(function(element){
// Do what you want with every element
});
}

Categories