I expect the following code to unload a javascipt jqgrid, then load another grid with different options, including different columns
//onload
(function($)
$.fn.myGridFn = function(options){
$(this).jqGrid('GridUnload');
$(this).jqGrid(options.gridoptions);
//....
$('#select').change(function(){
switch($(this).val())
{
case 'grid1':
$('#grid').myGridFn({gridoptions:{/*grid1 options*/}});
break;
case 'grid2':
$('#grid').myGridFn({gridoptions:{/*grid2 options*/}});
break;
}
});
})(jQuery);
//...
<table id="grid"></table>
What I get is the grid unloading, then I have to change the selection in the select element and back again to load the new grid.
Updated:
If I replace the $(this) in the plugin with the actual element selector $('#grid') - it works just fine, I cant do this in my real app because the plugin is used by several other table elements and grids
Cleaned up for future readers:
So here's a sort of working fiddle: http://jsfiddle.net/s3MsW/10/
I say "sort of" because the underlying code is suspect (jqGrid itself). But we'll get there in a moment... first thing: if you log "this" for the plugin, it's actually the jQuery object, not the node. Theoretically we can replace $(this) in your original code with this and all should work.
Except not.
You can in fact use this to unload the Grid, but then the function leaves this as a reference that does not point to the table on the rendered page. There are ways to show that the old node is still around ( http://jsfiddle.net/s3MsW/8 was a test ) but suffice it to say it can no longer be used to render a new table to the page proper.
There's no real choice except to cache the selector string and re-select the clean table (ie. create a new jQuery object) from scratch:
$.fn.myGridFn = function(options){
var theId = this.selector;
this.jqGrid('GridUnload'); // reference works for now
$(theId).jqGrid(options); // reference is broken, so re-select with cached ID
}
If you're conscientious about memory usage, you probably want to destroy this (the ghost node), but there's probably no real harm just keeping it around.
It seems to me that you should just save $(this) in a variable like $this and use it later. The problem is just that inside of
$('#select').change(function(){/*here*/}); // another value of this
so you should do
(function($)
$.fn.myGridFn = function(options) {
var $this = $(this), selector = $this.selector;
$this.jqGrid('GridUnload');
$this = $(selector); // reset $this value
...
$('#select').change(function() {
switch($(this).val()) { // here is $('#select')
case 'grid1':
$this.myGridFn({gridoptions:{/*grid1 options*/}});
...
Additionally one use typically start the body of plugin with
return this.each( function() { ...
to be sure that your plugin works also in the case of usage like $(".myGridClass").myGridFn(...) where one can have more as one element in wrapped set $(".myGridClass").
This issue stumped and the answer above was right on.
I kept trying to execute the following:
this.jqGrid('GridUnload')
this.('getGridParam'); /* Still returning all the parameters for the grid. */
Instead I did:
var $t = $(this.selector);
$t.jqGrid('GridUnload');
$t = $(this.selector);
$t.jqGrid('getGridParam'); /* Now empty */
I think you should try
$('#select option:selected).val()// gives the value of the selected option.
$('#select option:selected).text()// gives the text of the selected option.
instead of
$(this).val()
in the parenthesis of switch
Related
Lets say I have an empty div:
<div id='myDiv'></div>
Is this:
$('#myDiv').html("<div id='mySecondDiv'></div>");
The same as:
var mySecondDiv=$("<div id='mySecondDiv'></div>");
$('#myDiv').append(mySecondDiv);
Whenever you pass a string of HTML to any of jQuery's methods, this is what happens:
A temporary element is created, let's call it x. x's innerHTML is set to the string of HTML that you've passed. Then jQuery will transfer each of the produced nodes (that is, x's childNodes) over to a newly created document fragment, which it will then cache for next time. It will then return the fragment's childNodes as a fresh DOM collection.
Note that it's actually a lot more complicated than that, as jQuery does a bunch of cross-browser checks and various other optimisations. E.g. if you pass just <div></div> to jQuery(), jQuery will take a shortcut and simply do document.createElement('div').
EDIT: To see the sheer quantity of checks that jQuery performs, have a look here, here and here.
innerHTML is generally the faster approach, although don't let that govern what you do all the time. jQuery's approach isn't quite as simple as element.innerHTML = ... -- as I mentioned, there are a bunch of checks and optimisations occurring.
The correct technique depends heavily on the situation. If you want to create a large number of identical elements, then the last thing you want to do is create a massive loop, creating a new jQuery object on every iteration. E.g. the quickest way to create 100 divs with jQuery:
jQuery(Array(101).join('<div></div>'));
There are also issues of readability and maintenance to take into account.
This:
$('<div id="' + someID + '" class="foobar">' + content + '</div>');
... is a lot harder to maintain than this:
$('<div/>', {
id: someID,
className: 'foobar',
html: content
});
They are not the same. The first one replaces the HTML without creating another jQuery object first. The second creates an additional jQuery wrapper for the second div, then appends it to the first.
One jQuery Wrapper (per example):
$("#myDiv").html('<div id="mySecondDiv"></div>');
$("#myDiv").append('<div id="mySecondDiv"></div>');
Two jQuery Wrappers (per example):
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').html(mySecondDiv);
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').append(mySecondDiv);
You have a few different use cases going on. If you want to replace the content, .html is a great call since its the equivalent of innerHTML = "...". However, if you just want to append content, the extra $() wrapper set is unneeded.
Only use two wrappers if you need to manipulate the added div later on. Even in that case, you still might only need to use one:
var mySecondDiv = $("<div id='mySecondDiv'></div>").appendTo("#myDiv");
// other code here
mySecondDiv.hide();
if by .add you mean .append, then the result is the same if #myDiv is empty.
is the performance the same? dont know.
.html(x) ends up doing the same thing as .empty().append(x)
Well, .html() uses .innerHTML which is faster than DOM creation.
.html() will replace everything.
.append() will just append at the end.
You can get the second method to achieve the same effect by:
var mySecondDiv = $('<div></div>');
$(mySecondDiv).find('div').attr('id', 'mySecondDiv');
$('#myDiv').append(mySecondDiv);
Luca mentioned that html() just inserts hte HTML which results in faster performance.
In some occassions though, you would opt for the second option, consider:
// Clumsy string concat, error prone
$('#myDiv').html("<div style='width:'" + myWidth + "'px'>Lorem ipsum</div>");
// Isn't this a lot cleaner? (though longer)
var newDiv = $('<div></div>');
$(newDiv).find('div').css('width', myWidth);
$('#myDiv').append(newDiv);
Other than the given answers, in the case that you have something like this:
<div id="test">
<input type="file" name="file0" onchange="changed()">
</div>
<script type="text/javascript">
var isAllowed = true;
function changed()
{
if (isAllowed)
{
var tmpHTML = $('#test').html();
tmpHTML += "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').html(tmpHTML);
isAllowed = false;
}
}
</script>
meaning that you want to automatically add one more file upload if any files were uploaded, the mentioned code will not work, because after the file is uploaded, the first file-upload element will be recreated and therefore the uploaded file will be wiped from it. You should use .append() instead:
function changed()
{
if (isAllowed)
{
var tmpHTML = "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').append(tmpHTML);
isAllowed = false;
}
}
This has happened to me . Jquery version : 3.3.
If you are looping through a list of objects, and want to add each object as a child of some parent dom element, then .html and .append will behave very different. .html will end up adding only the last object to the parent element, whereas .append will add all the list objects as children of the parent element.
I have used firebug and IE profilers and can see what function in my code is causing the slowness. Being new to jquery, the recommendations that I have read online are not clear to me. I have made an example page that shows the slow behavior when you check or uncheck a check box. No surprise that this is fast using Chrome.
The function that is slow can be found on line 139.
$('.filters input').click( function()
JSFiddle can be found here
The code is 122 KB and can be found here
UPDATE: if you know of any examples online that are similar in function and faster, please share.
i had a brief look through your code, but it was very hard to follow. it seemed as if you were looping through things many many times. i used a much simpler approach to get the list of all states.
your approach was
* make a massive string which contained every class (possibly repeated multiple times)
* chop it up into an array
* loop through the array and remove duplicates
i simply took advantage of the fact that when you select something in jQuery you get a set rather than a single item. you can therefore apply changes to groups of object
$(document).ready(function () {
//this will hold all our states
var allStates = [];
//cache filterable items for future use
var $itemsToFilter = $(".filterThis");
//loop through all items. children() is fast because it searches ONLY immediate children
$itemsToFilter.children("li").each(function() {
//use plain ol' JS, no need for jQuery to get attribute
var cssClass = this.getAttribute("class");
//if we haven't already added the class
//then add to the array
if(!allStates[cssClass]) {
allStates[cssClass] = true;
}
});
//create the container for our filter
$('<ul class="filters"><\/ul>').insertBefore('.filterThis');
//cache the filter container for use in the loop
//otherwise we have to select it every time!
var $filters = $(".filters");
// then build the filter checkboxes based on all the class names
for(var key in allStates) {
//make sure it's a key we added
if(allStates.hasOwnProperty(key)) {
//add our filter
$filters.append('<li><input class="dynamicFilterInput" type="checkbox" checked="checked" value="'+key+'" id="filterID'+key+'" /><label for="filterID'+key+'">'+key+'<\/label><\/li>');
}
}
// now lets give those filters something to do
$filters.find('input').click( function() {
//cache the current checkbox
var $this = $(this);
//select our items to filter
var $targets = $itemsToFilter.children("li." + $this.val());
//if the filter is checked, show them all items, otherwise hide
$this.is(":checked") ? $targets.show() : $targets.hide();
});
});
FIDDLE: http://jsfiddle.net/bSr2X/6/
hope that's helpful :)
i noticed it ran quite a bit slower if you tried to slideup all the targets, this is because so many items are being animated at once. you may as well just hide them, since people will only see the ones at the top of the list slide in and out of view, so it's a waste of processor time :)
EDIT: i didn't add logic for show all, but that should be quite a trivial addition for you to make if you follow how i've done it above
You could use context with your selector:
$('.filters input', '#filters_container').click(function()...
this limits the element that jQuery has to look in when selecting elements. Instead of looking at every element in the page, it only looks inside your $('#filters_container') element.
I need a tree menu. But instead of a listview where you expand/collapse i need a dropdown box with the list and when you click on a element i need the box to update (with the first entry being 'Back') so the menu stays in a neat little dialog.
Does this menu have a name? Does anyone know where i can get code to do this?
I can think of several jQuery plugins which would soot your purposes. However, I would recommend jQuery iPod Style Drilldown Menu (Newer Version), which is exactly what it sounds like. The dropdown box updates in place, uses a cool sideways slide animation, and includes a "Back" button (as you desired). Finally, if you don't want any animation, you can try tweaking the plugin's many options. Setting crossSpeed to 0 may work, for example.
Adam is right, jQuery offers an assortment of menu's which you could use. Really though, this is a somewhat trivial problem, the code to write it would take up about 1/10th the space that jQuery's code will. So if possible I would say write it without jQuery.
The most effective method would be to do it JS OOP (Javascript Object-Oriented), but understandably this is a confusing topic.
Basically you just want something like:
function drillDown(){
//Any code that multiple drilldowns
// might need on the same page goes here
//Every instance of a drillDown will
// instantiate a new set of all functions/variables
// which are contained here
//A reference to the parent node the dropdown is placed in
this.parent;
//A reference to the div the dropdown is incased in
this.object;
//Returns a reference to this object so it can be
// stored/referenced from a variable in it's
// superclass
return this;
}
//Prototype Functions
//prototypes are shared by all
// instances so as to not double up code
//this function will build the dropdown
drillDown.prototype.build = function(parent){
//Too lazy to write all this, but build a div and your select box
// Add the select box to the div,
// Add the div to the parent (which is in your document somewhere)
var divEle = document.createElement('div');
var inputBox = document.createElement('input');
//code code code
divEle.appendChild(inputBox);
parent.appendChild(divEle);
}
//this function loads the newest dataset of
drillDown.prototype.loadNewDataSet = function(data){
//first clear out the old list
// remember we have a reference to both the
// 'object' and 'parent' by using
// this.object and this.parent
//load the data, we are going to use the text from
// the select boxes to load each new dataset, woo eval();
// If you didn't know, eval() turns a string into JS code,
// in this case referencing an array somewhere
var dataSet = eval(data);
//then loop through your list adding each new item
for(item in dataSet){
//add item to the list
//change the .onClick() of each one to load the next data set
// a la ->
selectItem.onClick = function(){this.loadNewDataSet(item);};
//if you name your datasets intelligently,
// say a bunch of arrays named for their respective selectors,
// this is mad easy
}
}
//Then you can just build it
var drillDownBox = new drillDown();
drillDownBox.build(document.getElementsByTagName('body')[0]);
drillDownBox.loadNewDataSet("start");
//assuming your first dataset array is named "start",
// it should just go
And by the way, Adam also said it, but wasn't explicit, this is refered to as a drill-down.
How can i use javascript (i assume) to clone a table row like ive beautifully illustrated in the picture below?
You can hookup a live event to all the buttons. If you give them a class of clone for instance the following will work.
$('input.clone').live('click', function(){
//put jquery this context into a var
var $btn = $(this);
//use .closest() to navigate from the buttno to the closest row and clone it
var $clonedRow = $btn.closest('tr').clone();
//append the cloned row to end of the table
//clean ids if you need to
$clonedRow.find('*').andSelf().filter('[id]').each( function(){
//clear id or change to something else
this.id += '_clone';
});
//finally append new row to end of table
$btn.closest('tbody').append( $clonedRow );
});
Please Note:
If you have elements in the table row with id's you will need to do a .each through them and set them to a new value otherwise you will end up with duplicate id's in the dom which is not valid and can play havoc with jQuery selectors
You can do this like so
If you want a really simple solution, just use innerHTML:
var html = document.getElementById("the row").innerHTML;
var row = document.createElement('p');
row.innerHTML= html;
document.getElementById("table id").appendChild(row);
For what purpose do you want to use the data? I've done similar things previously on data input forms and generally I've found it to be beneficial to the users not to manipulate everything in Javascript but to hook to store the data on the server and interface with AJAX.
The issue is that as soon as you start letting users do this sort of complex table manipulation and they accidentally hit the back button you end up with a lot of disgruntled punters. Coding up transient storage on a database isn't that much harder than manipulating Javascript and in fact can be easier as you can break the operations down more easily. Debugging is simpler for that reason too (you always have inspect access to the current state of your table).
Lots of option for handling via AJAX - the simplest being to just use a place-holder division and feed the whole table structure in as needed.
A jQuery example:
$(document).ready(function(){
$('TABLE.recs').delegate('INPUT.clone','click',function(e){
e.preventDefault();
var s = $(this).parent().parent().clone().wrap('<div>').parent().html();
$('TABLE.recs TBODY TR:last').after(s);
});
});
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