I have been tracking down a nasty bug in my code over the last 6 hours. Here is the scenario. I have a section of nodes on my webpage that has fields and those fields handle events from the user. For example, I have a name field that when typed into there is an autocomplete box that pops up. The user has the ability to duplicate the entire section of nodes so that they can create more items. I am using my own node copy code which consists of taking the innerHTML from the first node and parsing out some things, incrementing ids and then plugging it back into the system by appending it after the original node. I am using jQuery for the event handling, but not for the node copy, since the node copy has some custom stuff that I have to do.
The bug occurs only on IE when I copy the nodes and then try to fire an event by typing in the field. What happens is that the event handler for the SUBSEQUENT input boxes can not be fired until I modify the field in the ORIGINAL node block. Then I get BOTH events!!! Nasty huh?
The solution actually lies in jQuery’s clone function. Here you see that when they do a clone they will eliminate the property that is embedded in the html called jQuery12341234=”2″ (those number are some internal jQuery identifier, probably for the event handler).
To fix the bug all I had to do was remove that attribute and definition and viola it works!
Here is jQuery’s replace from their clone function.
html.replace(/ jQuery\d+="(?:\d+|null)"/g, "")
My approach (before I found that jQuery was doing it also was this.
html.replace(/jQuery\d+=["']\d+["']/g, "")
Since jQuery programmers know more than me (i.e. when null might be in that field) I just ditched mine and used theirs.
Have a great day!
Eric
I am using jquery with the Devbridge autocomplete plugin. It is an awesome tool. Unfortunately we had a bug reported that on IE6 the autocomplete box does not correctly cover up the other controls on the page. There are select boxes below the field with the drop down and when everything else is covered up, these select boxes stick out like a sore thumb.
Normally you would use the bgiframe plugin for jquery to address this problem, but in this case we don’t have a reference to the dropdown list once it is filled in from the ajax call. So, my solution was to add a simple callback to the autocomplete plugin that is fired off after the list is populated and made visible. That gives me the option that simply takes the list container and calls bgiframe on it. And viola! My dropdown list covers up all the controls underneath, even the select lists.
Here is a patch if you would like to patch the autocomplete plugin. Note, it also contains a fix from my last post about using multiple fields as input (the Devbridge autocomplete part is at the bottom of the post).
--- jquery.autocomplete.js 2009-09-27 23:15:14.000000000 -0600
+++ ../jquery.autocomplete.js 2010-01-29 15:29:14.000000000 -0700
@@ -230,6 +230,9 @@
},^M
^M
getSuggestions: function(q) {^M
+ if (this.options.noCaching) {^M
+ this.clearCache();^M
+ }^M
var cr, me;^M
cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q];^M
if (cr && $.isArray(cr.suggestions)) {^M
@@ -280,6 +283,7 @@
}^M
this.enabled = true;^M
this.container.show();^M
+ if ($.isFunction(me.options.onListShow)) { me.options.onListShow(this.container); }^M
},^M
^M
processResponse: function(text) {^M
Download the actual patch right here! jquery.autocomplete.js.patch
Remember, to patch your file, simply cd to where your jquery.autocomplete.js file resides (and copy in this patch to that directory to a file called jquery.autocomplete.js.patch) and run this command
patch jquery.autocomplete.js < jquery.autocomplete.js.patch
Now, once you have that file patched, you can do this sort of thing with your autocomplete options.
$("input[name*=some_name], input[name*=some_other_name]").each(function(i) {
$(this).autocomplete(
{
serviceUrl:baseURL + '/lib/dostuffremotely',
minChars:3,
width:500,
maxHeight:400,
zIndex: 9876,
deferRequestBy: 0, //milliseconds
params: {
'prefix':'xyz',
'column':'abc'
},
onListShow: function(container) { container.bgiframe(); }, // LOOK HERE, THIS IS NEW!!!
onSelect: function(value, data){ // "this" is NOT defined here
// handle onselect
}
}
);
Happy jquerying!
Eric
15
jquery autocomplete using values from other fields
0 Comments | Posted by eric in programming
This is in response to a question asked about a jquery autocomplete plugin.
The question was, if you are using this jquery autocomplete plugin how do you get the .autocomplete function to include other fields so that you can have a context type of search. One person (bendewey) answered that you can do the following.
var selectedCategory = $('.categories').val();
var query = '';
if (selectedCategory !== 0)
{
query = '?category=' + selectedCategory;
}
$("#suggest4").autocomplete('search_service.svc' + query, {
// options
});
This is 1/2 correct. The problem is that when .autocomplete() is called, the url + query are evaluated immediately which means that the current (at time of page loading most likely) selected category value will ALWAYS be used when autocomplete is called. That means that if the user selected a different category, the new value will not be passed with the query. Here is a patch to the autocomplete code that will fix it. After running the patch, you will be able to pass a function to your .autocomplete call (instead of a string url) and then each time autocomplete runs, the function will be run to create the right params.
to run patch:
1. cd to the directory where your jquery.autocomplete.js file exists
2. copy the patch file to a file called jquery.autocomplete.js.patch (in the above mentioned directory)
3. run patch < jquery.autocomplete.js.patch
to use the patch:
do what bendewey said above, except for you need to make the 1st arg of .autocomplete to be a function that returns the proper query, like this…
$("#suggest4").autocomplete(function() {
var selectedCategory = $('.categories').val();
var query = '';
if (selectedCategory !== 0)
{
query = '?category=' + selectedCategory;
}
return 'search_service.svc' + query; }, {
// options
}
);
Here is the patch (you can download the file below):
--- jquery.autocomplete.js.orig 2010-01-15 11:00:55.000000000 -0700
+++ jquery.autocomplete.js 2010-01-15 11:04:02.000000000 -0700
@@ -14,7 +14,7 @@
$.fn.extend({
autocomplete: function(urlOrData, options) {
- var isUrl = typeof urlOrData == "string";
+ var isUrl = typeof urlOrData == "string" || typeof urlOrData == "function";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
@@ -346,10 +346,14 @@
term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
+ var url = options.url;
+ if (typeof url == "function") {
+ url = options.url();
+ }
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
- } else if( (typeof options.url == "string") && (options.url.length > 0) ){
+ } else if( (typeof url == "string") && (url.length > 0) ){
var extraParams = {
timestamp: +new Date()
@@ -364,14 +368,16 @@
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
- url: options.url,
+ url: url,
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
success: function(data) {
var parsed = options.parse && options.parse(data) || parse(data);
- cache.add(term, parsed);
+ if (typeof options.url != "function") {
+ cache.add(term, parsed);
+ }
success(term, parsed);
}
});
You can also download the patch file here: jquery.autocomplete.js.patch
Now, what if you are using this autocomplete from Devbridge? You can almost do it out of the box, here is a code sample.
$("input[name*=phone_number]").each(function(i) {
var prefix = this.id.replace(/\.[^.]*$/, "");
$(this).autocomplete(
{
serviceUrl:baseURL + '/lib/findUsers',
zIndex: 9999,
deferRequestBy: 0, //milliseconds
noCaching: true,
params: {
'area':function() { return $(jq(prefix + ".area_id")).val(); },
'prefix':prefix
},
onSelect: function(value, data){ // NOTE: 'this' is NOT defined correctly here
// do stuff with data
}
}
);
});
You will notice that I included a parameter that isn’t supported, that is the noCaching parameter. I had this working pretty good without it, except if you changed the non-autocomplete field (in this case area code), and you go back to the auto field and fail to find something, that is cached with the query, so when you change the area code, you get stuck because the query field got cached, and auto thinks that it should not try again. That is why I made this tiny patch to this version of autocomplete.
Index: jquery.autocomplete.js
===================================================================
RCS file: /opt/Repository/MobilSense/MobilSentry/Products/MobilSentry/Src/CSS/share/assets/jquery.autocomplete.js,v
retrieving revision 1.1
diff -u -r1.1 jquery.autocomplete.js
--- jquery.autocomplete.js 10 Jan 2010 06:46:45 -0000 1.1
+++ jquery.autocomplete.js 15 Jan 2010 23:20:55 -0000
@@ -230,6 +230,9 @@
},^M
^M
getSuggestions: function(q) {^M
+ if (this.options.noCaching) {^M
+ this.clearCache();^M
+ }^M
var cr, me;^M
cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q];^M
if (cr && $.isArray(cr.suggestions)) {^M
And you can download it jquery.autocomplete.js.patch.
19
Inspired Money Maker’s Breakdown before Breakthrough
0 Comments | Posted by eric in motivational, sharpen the saw
I just read an awesome blog post by Paul Piotrowski. In his article, Paul discusses the sudden difficulties that arise in our lives as a “breakdown” and how a breakdown precedes a “breakthrough” . The breakdown is a set of difficult circumstances or feelings or emotions or something that hits you all at once and it seems like you just can’t get out of it.
I have been going through a breakdown for a little while. I have been blessed to be able to still provide for my family during this time, but it has been harder lately than it has been in a long time. A bright light that I have had in all this is that I have felt inspiration from God that success, real success, life changing success is just around the corner. I am grateful for this, when times get hard, I think back to that inspiration and it elevates my spirit again.
I really like how Paul looks at this situation in his blog. I am just getting ready to break through a big wall and it takes a lot of effort to knock down my “building blocks” so that they can be rebuilt or restructured. I have thought about this for a while, years before reading the wise words from that blog in fact. About 8 years ago Jennfer and I began to see our children going through “breakdowns” and “breakthroughs” at different times in their lives. It seems like every 6 months or so our kids kind of freak out. In researching it, we learned that human development is like a building that was built of blocks. When you build a block castle with a set of 20 blocks and get it just perfect, then suddenly somebody gives you another 20 blocks, you can’t just build onto your existing structure. You will likely have to tear down what you have, and taking your knowledge and experience in building the first one, now you will build one that is twice as large and beautiful.
My kids are amazing people, when this happens to them, after we get through the breakdown (this can be very painful) things are so much better. As with them, I know my own life is about to be amazing, because I have been in a larger than normal breakdown. But I am learning so much during this breakdown. So much more than I ever could have learned without it. As painful as it has been, I am thankful to God for giving me this opportunity to struggle and learn so that my “structure” can be rebuilt and I can be a better person, more successful and more in control of myself. I am so excited to see what things will look like when the rebuilding is done.
Eric
I just got a MyTouch phone. It is from HTC and runs the Google Android Operating System. All I can say is WOW! Actually I can say more and I will. I will surely write more about this amazing phone, but for now I will just say WOW.
Eric
Great Reminder of the Importance Of Money
I follow an interesting blog that focuses on money from the perspective of Ayn Rand and her amazing book “Atlas Shrugged”. The posted a great article that I want to link to because this speech by Francisco is so important and so applicable in our day.
My Money Shrugged — Gold And Money
Thanks guys!
One of the most annoying things with javascript is trying to see what is inside of an object. In python you simply print (‘attributes=%s’%dir(obj)), and in php you simply use var_dump(obj), or if you want to format it into a string, you can use echo “attributes=” . var_export(obj) ; But with Javascript I really haven’t had an easy way to introspect the attributes of an object. Until now.
I have been using a great tool for debugging javascript called FireBug, it is a plugin for firefox. So, firebug has a way to do object introspection via the DOM tab. When you get an object that you want to analyze simply do something like this in your code (at a global level) var MYOBJECTREFERENCE = new Object(); Now, wherever you have access to that object you want to introspect (in an event handler or whatever), you simply say MYOBJECTREFERENCE.XYZ = obj; Then, refresh, trigger whatever event it took to generate obj, and then look in firebug’s DOM explorer window for MYOBJECTREFERENCE.XYZ.
YAY! Now there is an easy way to introspect objects in javascript. If you know of other ways to do this, please post them for us all to see.
Eric
UPDATE: 12/8/2009
I found another way to do this. The code is from RefactorMyCode.com . Mine is a little different because I was getting errors and I had to protect against them a bit… Here you go!
function odump(obj, depth, max) {
depth = depth || 0;
max = max || 2;
if (depth > max)
return false;
var indent = "";
for (var i = 0; i < depth; i++)
indent += " ";
var output = "";
for (var key in obj){
// skiplist
if (key == "domConfig" || key == "selectionStart" || key == "selectionEnd" || key == "schemaTypeInfo") {
output += "\n" + indent + key + "**: ";
} else {
try {
output += "\n" + indent + key + ": ";
switch (typeof obj[key]){
case "object": output += odump(obj[key], depth + 1, max); break;
case "function": output += "function"; break;
default: output += obj[key]; break;
}
} catch(err) {
alert("add key=" + key + " to your skiplist because of: err=" + err);
}
}
}
return output;
}
This is from last week at Solitude. I have since gotten a new board that is amazing! I got an Escape from neivasnowboards, it rocks. I will do a review and pictures of it later. But for today, here is early season photos from my first trip. Note, I went yesterday and it was like 50 times better than these shots, so fun in fact that I forgot to pull out my phone and grab photos… Oh well
Headed up tomorrow to get the kids on the run and get them learning, so maybe I will take a few more pix and put them on here.
Eric
I had to write something today in javascript that used for loops. No big deal, but usually I put it into a separate .js file, in this case I had to put it right there in the html. This created a little problem that I thought I would write about to help remind myself and provide a little guidance to others.
Less Than ‘Test’ In HTML
The problem arises when the html parser gets involved and runs into the javascript which was supposed to be ignored. Usually I will put almost all the javascript into a separate .js file so that things are cleaner, but in this case, I just need to do a quick loop right in the html. So, I used a <script tag. Unfortunately for me, the i<n loop invariance test (the end of loop test) uses a symbol (<) which is the start of an html tag open or close. I don’t know if this problem occurs when using a simple html file or not, but I am using xhtml and it really broke things.
Here is the header of my xhtml file.
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
Here is my for loop code.
<script type="text/javascript">
for (var i=0; i<10; i++) {
alert("test: " + i);
}
</script>
Well the xhtml parsing freaks out when it sees the ‘i<10′ part, because < is obviously looking similar to a tag opening or closing. So at first I tried to put an html comment around it using <!– XXX –> but then my javascript wasn’t even being executed. So I had to eventually use a CDATA section like this.
<![CDATA[
for (var i=0; i<10; i++) {
alert("test: " + i);
}
]]
This was a good solution that worked for me.
Once I got the loop working, I encountered another problem.
Iterating Over Arrays Can Be Weird
In Java or C or C++ or python etc, we typically iterate over each item in an array and index with a counter. We just stop looking when the index has reached the max number of elements (minus 1) in that array.
In javascript things are different because arrays are really objects with properties. Therefore if you are trying to use the .lenght attribute (like java) you may be surprised. What I found was that
I was getting as the last item in the array an ‘undefined’ element.
for (var i=0; i<all_inputs.length;i++) {
alert("input: " + i + ") " + all_inputs[i]);
}
So, the solution is to do everything the same except for the loop invariance test. Here we just say to loop while the i’th element is not undefined. In other languages, you know that this will generate a array out of bounds type exception, but since this is really checking properties of an object, things are a little different.
for (var i=0;undefined != all_inputs[i];i++) {
alert("input: " + i + ") " + all_inputs[i]);
}
And, those are just some handy things to remember next time you are making for loops in your html code and iterating over array elements.
for (var i=0; i<10; i++) { alert("test: " + i); }
How To Delete Annoying Twitter Direct Messages
If you have used twitter for very long then you know that some people really enjoy sending direct messages (DMs). This is very annoying to me because they are usually just spam and the person wants me to click on their ad or something. Now, don’t get me wrong, I am not opposed to ads. In fact a targeted ad for something that I am interested in is very valuable to me. I just don’t like broadcast spam that fills my inbox. I am not kidding when I tell you that I had to delete 2,003 DMs from my twitter account today. It was a pain at first, until I built a little tool to do it. Now, from my tool it is very easy. This tool is a python script that you run from the command line. Remember, that python must be in your PATH for this to work, if it is not, then you will have to add it, or run python by specifying the whole path. This will work Linux, Windows, Mac, etc, you just have to install python first.
So, here is how you delete all DMs.
python twitter_tool.py delete myuser mypass all
How To Run The Twitter List Messages / Delete Messages
Obviously my script is called twitter_tool.py here are the syntax help messages.
syntax: twitter_tool.py [list | delete ] twituser twitpass [ maxmessages | all ]
In the syntax message, you can list or delete all your messages, or up to a certain number of messages. A nice way to run it is where you view 10 messages, then delete them, then repeat. Like this…
$ python twitter_tool.py list myuser mypass 10 from: blablabla date: Sat Nov 14 22:53:14 +0000 2009 You Can Find It Here http://somespammylink.spam ----------------------------------------- $ python twitter_tool.py delete myuser mypass 10 deleting message 559771108 from blablabla $
How To Run The Twitter Unfollow Command
Now what if I am cruising through my messages and I spot something that is really offensive or spammy and I just want to get away from that user. I can easily unfollow them with the same tool. Here is an example.
python twitter_tool.py unfollow myuser mypass SomeAnnyoingUser
The End
Well, that is it. A simple tool that does 3 jobs. And you are welcome to download and use the tool.
Click here to get twitter_tool.py
*** UPDATE *** Click here to get twitter_tool.py that has the ability to search for other twitter users… For example, to search for people with the word snowboard in their name, you would do this
python bin/twitter_tool.py searchpeople myuser mypass snowboard
NOTE: you will probably have to Right Click -> Save As to get the file, then save it as twitter_tool.py (i.e. you must remove the .txt extension).
Follow @bigconcept on Twitter





