Search code examples
javascriptprototypejsdhtml

Best technique for DHTML windows/dialogs in Prototype?


I love Prototype but it doesn't come with any widgets. In particular, it provides nothing for window/dialog creation.

I've tried the Prototype Window add-on library but found it unreliable and it hasn't been maintained for a long time.

Anyone have any recommended solutions? Or any recipes/patterns for creating a dialog from first principles (i.e. with no library other than Prototype)? I need modal behavior (with faded background effect) and I have to support IE6 as well as modern browsers.

Just to be clear, I'm not looking to switch away from Prototype and I don't really want to include an additional base library such as JQuery.


Solution

  • For a really simple dialog, checkout this code: http://snippets.dzone.com/posts/show/3411

    Very minimal and simple - just what I wanted.

    The code went through a few modifications. Here's the final version that worked for me:

    /* 
     * Adapted from http://snippets.dzone.com/posts/show/3411
     */
    
    var Dialog = {};
    Dialog.Box = Class.create();
    Object.extend(Dialog.Box.prototype, {
      initialize: function(id) {
        this.createOverlay();
    
        this.dialog_box = $(id);
        this.dialog_box.show = this.show.bind(this);
        this.dialog_box.persistent_show = this.persistent_show.bind(this);
        this.dialog_box.hide = this.hide.bind(this);
    
        this.parent_element = this.dialog_box.parentNode;
    
        this.dialog_box.style.position = "absolute";
    
        var e_dims = Element.getDimensions(this.dialog_box);
        var b_dims = Element.getDimensions(this.overlay);
    
        this.dialog_box.style.left = ((b_dims.width/2) - (e_dims.width/2)) + 'px';
        this.dialog_box.style.top = this.getScrollTop() + ((this.winHeight() - (e_dims.width/2))/2) + 'px';
        this.dialog_box.style.zIndex = this.overlay.style.zIndex + 1;
      },
    
      createOverlay: function() {
        if ($('dialog_overlay')) {
          this.overlay = $('dialog_overlay');
        } else {
          this.overlay = document.createElement('div');
          this.overlay.id = 'dialog_overlay';
          Object.extend(this.overlay.style, {
            position: 'absolute',
            top: 0,
            left: 0,
            zIndex: 90,
            width: '100%',
            backgroundColor: '#000',
            display: 'none'
          });
          document.body.insertBefore(this.overlay, document.body.childNodes[0]);
        }
      },
    
      moveDialogBox: function(where) {
        Element.remove(this.dialog_box);
        if (where == 'back')
          this.dialog_box = this.parent_element.appendChild(this.dialog_box);
        else
          this.dialog_box = this.overlay.parentNode.insertBefore(this.dialog_box, this.overlay);
      },
    
      show: function(optHeight/* optionally override the derived height, which often seems to be short. */) {
        this.overlay.style.height = this.winHeight()+'px';
        this.moveDialogBox('out');
    
        this.overlay.onclick = this.hide.bind(this);
    
        this.selectBoxes('hide');
        new Effect.Appear(this.overlay, {duration: 0.1, from: 0.0, to: 0.3});
        this.dialog_box.style.display = '';
    
        this.dialog_box.style.left = '0px';
    
        var e_dims = Element.getDimensions(this.dialog_box);
    
        this.dialog_box.style.left = (this.winWidth() - e_dims.width)/2 + 'px';
    
        var h = optHeight || (e_dims.height + 200);
        this.dialog_box.style.top = this.getScrollTop() + (this.winHeight() - h/2)/2 + 'px';
      },
    
      getScrollTop: function() {
        return (window.pageYOffset)?window.pageYOffset:(document.documentElement && document.documentElement.scrollTop)?document.documentElement.scrollTop:document.body.scrollTop;
      },
    
      persistent_show: function() {
        this.overlay.style.height = this.winHeight()+'px';
        this.moveDialogBox('out');
    
        this.selectBoxes('hide');
        new Effect.Appear(this.overlay, {duration: 0.1, from: 0.0, to: 0.3});
    
        this.dialog_box.style.display = '';
        this.dialog_box.style.left = '0px';
        var e_dims = Element.getDimensions(this.dialog_box);
        this.dialog_box.style.left = (this.winWidth()/2 - e_dims.width/2) + 'px';
      },
    
      hide: function() {
        this.selectBoxes('show');
        new Effect.Fade(this.overlay, {duration: 0.1});
        this.dialog_box.style.display = 'none';
        this.moveDialogBox('back');
        $A(this.dialog_box.getElementsByTagName('input')).each( function(e) {
          if (e.type != 'submit' && e.type != 'button') e.value = '';
        });
      },
    
      selectBoxes: function(what) {
        $A(document.getElementsByTagName('select')).each(function(select) {
          Element[what](select);
        });
    
        if (what == 'hide')
          $A(this.dialog_box.getElementsByTagName('select')).each(function(select){Element.show(select)})
      },
    
      winWidth: function() {
        if (typeof window.innerWidth != 'undefined')
           return window.innerWidth;
        if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0)
           return document.documentElement.clientWidth;
        return document.getElementsByTagName('body')[0].clientWidth
      },
      winHeight: function() {
        if (typeof window.innerHeight != 'undefined')
          return window.innerHeight
        if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientHeight != 'undefined' && document.documentElement.clientHeight != 0)
          return document.documentElement.clientHeight;
        return document.getElementsByTagName('body')[0].clientHeight;
      }
    
    });