7 posts tagged “javascript”
So Im working on this other project now and I am faced with the following situation:
- A server script renders an element's onclick handler as calling a function with an array as its parameter, e.g.
<div onclick="some_function(this, new Array('1','2','3'));">Some content</div> - Everytime some_function() is called, it needs to create another div after it but with the array altered, i.e.
<div onclick="some_function(this, new Array('1','2','3'));">Some content</div>
<div onclick="some_function(this, new Array('2','3'));">Content generated by some_function()</div> - Eventually, the array has no elements left and what I want left is:
<div onclick="some_function(this, new Array('1','2','3'));">Some content</div>
<div onclick="some_function(this, new Array('2','3'));">Content generated by some_function()</div>
<div onclick="some_function(this, new Array('3'));">Second content generated by some_function()</div>
<div>Last content generated by some_function()</div> - What does some_function() look like?
- "onclick" is not a DOM attribute, so you can't assign it a string like:
divElement.setAttribute("onclick", some_function_string);
Actually this does work in Firefox 2 but it may not work in other browsers (and thats the way it should be). - To assign an onclick handler, you specify a function not a string, i.e.
divElement.onclick = function() { some_function(this, new Array('1','2','3')); };
- Convert the function into a string, i.e. (in this case the function I'm interested in is the onclick handler of the divElement)
var onclick_string = divElement.onclick.toString; - Modify the string via string functions and regex (in this case I want to change the array being passed to some_function()):
// Set value to "1", "2", or "3" depending on which one we want to remove
var re_value = new RegExp("[\"']"+value+"[\"'][,]*");
onclick_string = onclick_string.replace(re_value, ""); - Modify the string to exclude everything but the function body:
onclick_text = onclick_text.replace(/[^{]*?{/, ""); // delete everything before and including first "{"
onclick_text = onclick_text.replace(/}[^}]*$/, ""); // delete everything after and including last "}" - Create a new Function object with the string as its body:
var new_onclick = new Function(onclick_text); - Now we have a modified function ready to be used!!
newDivElement.onclick = new_onclick;
- Convert the function to a string
- Do string manipulation to edit the function
- Convert the string back to a function
A very good place to start if you want to capture keyboard events with JavaScript: http://www.quirksmode.org/js/keys.html
Rule of thumb (i guess) is:
- Use keyDown or keyUp, not keyPress
- Use keyCode instead of charCode
- Use the link above and scroll to the bottom to check the keyCodes of various keys :)
Honestly, I think the next time I do anything in javascript, ill start by writing a class. Scoping is very important. Since goodness knows when, Ive always started writing programs in javascript as functions and them any variable is defined outside of it. And thats how it was for my mockup. So one year of that, and what I have is 4000 lines of hacking and global variables. Im actually quite surprised how I managed to make things still work. Lucky? Probably not. Maybe Im reallly careful in using my variables. Then again, I owe many things to the great debugging tools that i have (for Firefox).
Anyway, lesson of the day: to declare a local variable in a function in javascript, don't forget to precede the variable name with: var
Update: Apparently, Internet Explorer not only bugs out, it actually throws a seemingly unrelated runtime error saying that the Object doesn't support this property or method whenever I forget to include the 'var' keyword, e.g.:
Obviously, I thought the error meant that my object (node) doesn't support the getElementsByTagName method, which is ridiculous. I have no idea how I managed to think to see what happens if I added the 'var' keyword. Maybe I'm missing something here...tabs = document.getElementById('tabs').getElementsByTagName("li");
So Im in my final steps at polishing my system UI. And Im designing some tests for my tabbed interface. The problem with mockups is that some problems don't appear and hence you cant solve unless you can somehow think of them despite their non-existence. I guess thats why every product needs to be tested fully in its actual environment (or all possible environments) before actual release.
In my mockups, I didnt have any loading problems but now, with an actual backend, it takes at least 2 seconds before any data reaches the client. And the beauty of a tabbed interface, at least from the user's point of view, is that everything is in parallel. You expect each tab to be in its own neat little world. So now I have to deal with issues when multiple tabs are loading and when a user switches between tabs that are loading and tabs that are not. And maybe the user wants to do other things while a tab is loading, etc. After all, this is the beauty of Asynchronous communication, you can actually do things while data is being loaded.
So well, my modest test cases lists the possible scenarios in which loading occurs and the list of things that users can do while loading that can cause problems:
Loading scenarios:
Test cases:
- when click suggestion
- when new search
- when new tabbed search
- when sort/add/remove col
- when loading more results
- click other tab
- click other tab then return
- click sort
- click reserve
- start new tabbed search
Oh well, speaking of testing, here's Walt Mossberg from the Wall Street Journal and his 2 weeks of testing the iPhone:
So while I was happy as a bird using my Firebug and Web Developer toolbar add-on for debugging my system (read: Javascript bugs and errors), working with IE has no such luxuries. The closest thing to Web Developer toolbar for IE is the IE Developer toolbar. But then again, I personally feel that its not as powerful as Web Developer toolbar. But this is just half the story. There is no Firebug equivalent for IE! Well, there is Firebug Lite that has to be integrated into the source in the server (or use UserScripts via IE7Pro). But its not as powerful as the real Firebug. e.g. when theres an error in a script, normally I will use Firebug to open up exactly where the error occured and then use the watch list to debug some states (variable values, etc). With Firebug Lite, however, it just tells me that the error occured on Line 158. So I have to basically go through every single javascript that i have, insert a blank line at the top and refresh my browser until the error line changes (which would indicate that the recently changed file is the one in mention). I may not know how to use Firebug Lite and may be there is a way around this. But I cant seem to figure out how. Anyway, its more convenient in Firebug even if there is actually a way. The default error message from IE is just as non-descriptive. It just tells me the line number where the error occured.
I visited an MSDN blog (i think) and there are several suggestions to debug JavaScript in IE. One is by using Microsoft Script Editor (unfortunately I dont have Microsoft Office XP so I don't have this utility). Another is Microsoft Script Debugger (which I read is not very powerful). The last is the overkill of using Microsoft Visual Studios to debug (in which its actually debugging iexplorer.exe and some javascript errors detected in IE are not handed over to Ms Visual Studios for some reason -- its as if only the critical errors are passed over?).
Anyway, so Im going to debug using Firebug Lite along with the help of MSDN JScript documentation until I can find a better one. At least it has a console that I hope to be able to use to watch variables (the hard way by typing javascript like commands instead of variable names like in Firebug).
Update: I ended up using Ms Visual Studios after all for the run-time errors. As for the normal errors, I had to do a cat *.js > everything.js so that I know which line the error originated from. IE is a pain.
Update 2: I am subscribed to Apple's web dev mailing list and I read this related post about tools for web-dev in FF, IE and Safari. So here's the list: (Note that this is an ongoing discussion in the list)
> FF
> JavaScript Debugger : Venkman (Awesome), Firebug (OK)
> CSS and HTML Inspector: Firebug (The best)
> HTTP Request Inspector: Live HTTP Headers(Great), Firebug(Great)
> Profiler: Firebug (The best) Venkman (Good)
> DOM Inspector: Yes (Awesome)
>
> IE
> JavaScript Debugger: Visual Studio (The best)
> CSS and HTML Inspector: IE Developer Toolbar (OK)
> HTTP Request Inspector: HTTP Watch (The best)
> Profiler: none;
> DOM Inspector: none;
>
> Safari
> JavaScript Debugger: Drosera (Under development)
> CSS and HTML Inspector: none;
> HTTP Request Inspector: none;
> Profiler: none;
> DOM Inspector: Yes (Awesome)
So Im back from my trip to the server-side and landed myself back to client-side problems. The problem with mockups is that things appear to run much faster than they really do once you hook up a proper backend. Things run especially slower for me since my system involves scraping of web pages.
One major slow-down in my system is in how I handled text overflow. There were many interesting solutions on the web but I came up with my own computationally intensive solution. Here is the rough algorithm:
- Draw the text unwrapped and allowed to overflow by wrapping it in a left-floated div.
- Compare the widths of the div with its container (the td in my case) and decide whether or not to apply a "..." style (achieved by using a "..." background image and right padding the text so that it ends before the "..." background starts.)
- Its computationaly intensive because of the overhead: it has to introduce an intermediary div tag to compare widths (this is the only way I could think of to decide if the text was overflowing).
- When users resize the screen after the final render, the "..." doesn't update. Of course, we can make a call to the function on every screen resize, but thats just suicide.
I was satisfied with my solution at the time since I was only handling mock data. Now, its a different story. So I have come up with a CSS-based solution which hopefully works much better. I thought about this a few months ago but didnt really have time to implement it given the time frame for completing my thesis. Anyway, here's what I remember of the solution:
- The problem is how to use CSS to automatically detect:
- that there is overflow
- that the layout has resized and the "..." needs to shift
- For problem #1, we can try to push the "..." out of view whenever there is no overflow. Likewise, when there is overflow, the "..." is in view.
- Problem #2 can be solved by the above if we have a continous solution rather than a boolean (overflow or otherwise) solution.
- My solution involves using 3 levels of z-index:
- Bottom layer (furthest from viewer) holds the text
- Middle layer holds the "..." image
- Top layer holds a curtain for the "...", i.e. its a solid coloured div (same colour of background-color of text)
- We float both the top and bottom layer to the left so that as the text gets longer, the top layer gets pushed further to the right. e.g. <div class="floatleft" id="text">text</div><div class="floatleft" id="curtain"></div>
- We float the middle layer to the right.
- When the text is not overflowing, the curtain div is occluding the "..." layer. But when the text overflows, the text div will push the curtain div past the position of the "..." div and hence it is no longer occluded. But on the other hand, the text is occluded since the "..." layer is on top of the text layer.
- The margin of error for this solution is just the width of the "...", i.e. if the curtain is only pushed by so little that only half of the "..." is revealed. But this could be compensated by offsetting the curtain from the text by half the width of the "..." image.