Archive for the 'Javascript' Category

June 7th, 2022
9:03 am
Javascript console.log statement can be asynchronous/browser dependent

Posted under Angular & Javascript & Knowledge Base & Web
Tags ,

I had trouble when debugging some asynchronouse code – console.log was showing a later value/state of an object and not the current one at the time of the statement, which significantly confused the debugging process.

It turned out that console.log operation is browser dependent and not guaranteed to be either sync or async, and can also change between versions. In addition, when logging a complete object, the log statement might be synchronous, but it is only logging the object reference. The actual values will only be accessed when the object is opened in the chrome inspector for example.

These issues are discussed online here, here and here.

In the end I used breakpoints and inspected values directly when stopped on a breakpoint, rather than relying on the operation of console.log. This point needs to be borne in mind when dealing with async code, callbacks etc.

Comments Off on Javascript console.log statement can be asynchronous/browser dependent

May 10th, 2011
1:24 pm
Using Namespaces in Javascript to avoid naming collisions

Posted under Javascript
Tags , ,

Update 15/9/2011

Here is an example of my initial implementation, similar to the corejsf example above, but using a shorthand syntax to avoid the ifs. This only requires the 2 additional lines in front to declare the namespace. This can be done with global ‘register’ functions, but these in turn have namespacing issues and seem overly complex compared to the code below. Note also that this uses Javascript Object literal syntax to declare the functions (the ‘colon operator’ below, which is also used in JSON). This syntax in Javascript is a concise notation for name value pairs.

This implementation needs rolling out across all the other Javascript – this still needs doing.

var uk=uk||{}; uk.co=uk.co||{}; uk.co.salientsoft=uk.co.salientsoft||{};
uk.co.salientsoft.util= {
 clearElement: function(elementId) {
  var escapedId = ‘#’ + elementId.replace(/:/g, ‘\\3A ‘);
  jQuery(escapedId).val(”);
 }
};

Original Post

It is important to avoid collisions between Javascript function/variable names used by different objects – it is all too easy for entirely different pieces of code to be used in the same application and result in a name collision.

A good mechanism to avoid this for both functions and data is detailed in Core JavaServer Faces Edition 3, page 399. An excerpt from this page detailing how this is done is shown below.

 JavaScript Namespaces

In “The f:ajax Tag” on page 389, we implemented a JavaScript function—showProgress()—that uses Prototype to show and hide a DOM element. That method is fragile, however, because it can be overwritten by another JavaScript method of the same name.

It sounds plausible that someone might implement another JavaScript function named showProgress(), thereby replacing your version of the method with theirs. To prevent that replacement from happening, you could name your function something more unique, such as com.corejsf.showProgress(). With that strategy in mind for protecting our JavaScript method from being overwritten, we implement a simple map that serves as a namespace, and we define functions in that map**:

if (!com) var com = {}
if (!com.corejsf) {
    com.corejsf = {
        showProgress: function(data) {
            var inputId = data.source.id
            var progressbarId = inputId.substring(0, inputId.length – “name”.length) + “pole”;
            if (data.status == “begin”)
                Element.show(progressbarId);
            else if (data.status == “success”)
                Element.hide(progressbarId);
        }
    }
}

So now the caller accesses the function through the namespace, like this:

<f:ajax event=”blur” render=”nameError” onevent=”com.corejsf.showProgress”/>

JavaScript namespacing not only prevents others from overwriting your functions, but it also indicates to readers of your code that you are a JavaScript coder of some substance.

NOTE: Besides creating an ad-hoc namespace by putting JavaScript functions in a map, you can also put data in a map. Namespacing data is a consideration when you implement custom components that maintain data on the client. If you have multiple instances of the component, they will overwrite each other’s data. Putting the data in a map, keyed by the client identifier of the component to which the data is associated, is a way to ensure that multiple Ajax-based custom components of the same type that store data on the client, can co-exist in a single page.

** The map is actually an object literal—JavaScript does not have a map data structure—but semantically a JavaScript object literal is similar to a map.

No Comments »

March 25th, 2011
6:43 pm
Creating Javascript Objects using new – the pros and cons

Posted under Javascript
Tags , ,

There are a number of pros and cons regarding this practice, and they are debated on StackOverflow here. A w3schools tutorial on it may be found here.

I have used this e.g. here, following the lead of the Primefaces dialog.js. A downside is that your code fails if you forget the new and just call the function instead. However, this can be detected and an error thrown as per the StackOverflow post. In my case, the thrown error was not easily detected so I elected to pop an alert as well.

I do not discuss the issue further here – just follow the links and you will find plenty of information on it.

No Comments »

March 23rd, 2011
4:08 pm
Mapping Browser Keyboard events to Element Clicks

Posted under Javascript
Tags , , ,

I wanted to do this as a way to add keyboard accelerators, mainly for enter and escape when doing editing etc.

The code was already implemented to be driven from mouse click events, so causing keyboard events to soft click particular target buttons for save and cancel was convenient as this was then completely isolated from the rest of the code, which only had to be concerned with being button driven.

I found the following issues during the implementation:-

A keypress event was fine for trapping Enter, but it did not trap escape correctly. For escape, I needed to drive from the keyup event.

My enter event was bubbling up and causing unwanted navigation. I initially tried cancelling bubbling and propagation as detailed here on Quirksmode.org:-

function doSomething(e)
{
    if (!e) var e = window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
}

This involves catering for browser differences , as shown, as the Microsoft/IE model uses cancelBubble, whereas the W3C model uses stopPropagation. In my case, this did not solve the issue.

My simple solution was for the event handler to return false when it had trapped and handled a keyclick such as enter, and to return true otherwise. Returning false blocked any further action on the event, and was also a standard mechanism. I did not bottom out the actual problem in my case, but this solved it completely.

When implementing the solution, I used the following technique:-

  1. An object literal was created which did a reverse map from all the key codes I might want to trap, to a mnemonic name for them (such as enter, escape, leftArrow).
  2. My event handler call accepts a keyActions object literal argument where the names are the above mnemonics, and the values are the element IDs which need to be clicked when the matching key code is detected.
  3. In the event handler, I reverse lookup the key code to the mnemonic, and then look up the element Id in the passed object literal using the mnemonic.
  4. If I find an element Id, I click it and return false. If I do not find one, I return true as I am not trapping that key.
  5. I also try a lookup on the passed keyActions object using the raw key code. This means that the caller can either use a mnemonic or a key code to specify what key is handled.
  6. The event handler therefore has very little code, and does not need any loops – it just does 2 associative lookups to get the target element to click.
  7. As an additional feature, I allow an idPath key/value to be passed in keyActions. If this is present, any element value which is not prefixed with ‘#’ will be prefixed with the id path, to simplify the call site where a number of elements with a common ID prefix are specified. Elements already prefixed with ‘#’ are left untouched.

This technique would also work well when implementing global key handling attached to the <body> tag. In this case, it may be desirable to switch the global handling depending on what is open on the page. For example, there might be an in-page detail form which is opened in-place and client side using a Primefaces p:panel. In this case, the keyActions object could be switched when the detail panel is opened, and switched back when it is closed.

Another useful trick if the complexity warrants it could be to implement a stack such that detail panels on the page push their own keyActions onto the stack, perhaps inheriting the global ones and modifying them as required. When a detail level is closed, it can just pop the stack and does not need to know about the higher levels of actions on the page. To assist with this, it would be useful to be able to clone the previous level actions when adding a new level. Prototypes could be used for this, but they need constructor functions etc. and do not map well to our simple case which just uses JSON like object literals. The jQuery extend method could be a good choice for this, and is detailed here on StackOverflow by none other than Mr jQuery himself, John Resig (!)

The following example code fragment from a utility object illustrates the idea:-

var uk=uk||{}; uk.co=uk.co||{}; uk.co.salientsoft=uk.co.salientsoft||{};
uk.co.salientsoft.util= {
    clearElement: function(elementId) {
        jQuery(uk.co.salientsoft.util.escapeId(elementId)).val(”);
    },
    escapeId: function(elementId) {
        //a value tests true if it is non-null, non-blank, not NAN, not undefined, and not false
        var escapedId = (elementId) ? ((elementId.charAt(0) == ‘#’ ? ” : ‘#’)  + elementId.replace(/:/g, ‘\\3A ‘)) : ”;
        return escapedId;
    },
    keyCodeNames: {
        /*
         * Common list of keycodes, allowing reverse lookup of name from code.
         * Derived from: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
         * This only includes the common function/special keys which should be browser independant.
         */
        8:’backspace’,
        9:’tab’,
        13:’enter’,
        16:’shift’,
        17:’ctrl’,
        18:’alt’,
        27:’escape’,
        33:’pageUp’,
        34:’pageDown’,
        35:’end’,
        36:’home’,
        37:’leftArrow’,
        38:’upArrow’,
        39:’rightArrow’,
        40:’downArrow’,
        45:’insert’,
        46:’delete’,
        112:’f1′,
        113:’f2′,
        114:’f3′,
        115:’f4′,
        116:’f5′,
        117:’f6′,
        118:’f7′,
        119:’f8′,
        120:’f9′,
        121:’f10′,
        122:’f11′,
        123:’f12’       
    },
    clickOnKey: function(event, keyActions) {
        /*
         * This is a keyup/keydown/keypress event handler.
         * keyActions is an object literal containing key mnemonics for the keys, and target elements for the values.
         * As well as mnemonics, raw key code numbers can be used.
         * A special object literal key, idPath, may be used to specify a default ID prefix.
         * If present, it is applied to all element IDs which are not prefixed with a ‘#’. 
         * Examples:-
         *    onkeypress="return uk.co.salientsoft.util.clickOnKey(event, {enter:’frm1:cmdSaveNew:link’});"
         *    onkeyup="return uk.co.salientsoft.util.clickOnKey(event, {escape:’cmdCancelNew:link’, idPath:’frm1:’});"
         *
         * NOTE – escape handling does not work on a keypress event.
         * The event is detected, but the click fails to do anything.
         * onkeyup should be used for escape, as this does not suffer from the problem.
         */
        var keycode = (event.keyCode ? event.keyCode : event.which);
       
        /*
         * Try to fetch the target element id from the passed keyActions assuming a string key code name was used, e.g. ‘enter’
         * If this fails, try a lookup based on the raw key code.
         * Doing it this way avoids looping.
         */       
        var actionElement = keyActions[uk.co.salientsoft.util.keyCodeNames[keycode]];
        if (actionElement == null) {
            actionElement = keyActions[keycode];
        }       
       
        /* If found a target for the event keycode, click it
         * and return false to stop any propagation, else return true
         */
        if (actionElement != null) {
            if (actionElement.charAt(0) != ‘#’ && keyActions.idPath != null) {
                actionElement = keyActions.idPath + actionElement;
            }
            jQuery(uk.co.salientsoft.util.escapeId(actionElement)).click();
            return false;
        }
        return true;
    }
};

No Comments »