javascriptjquery

How to get exactly the same div elements order in my jQuery converted code from a working mootools code?


I am trying to convert a working mootools code into jQuery equivalent. Code is for a sticky sliding menu on top of web page and the conversion is needed just because the same code is to be used in a Joomla! version >= 4 based web site, where mootools has been dropped in favour of jQuery.

In principle, the converted jQuery code do its job, except that an added <div>...</div> element is inserted in a different place when comparing to the original mootools output and because of the different css order, the final style (a height in his case) gives a different cosmetic result.

This is the function code for each versions:

// mootools (original based)
setDesktopSticky: function(menuId) {
    var header = document.id(menuId ? menuId : 'custom-menu');

    window.addEvent('load', function() {
        var headerPosition = header.getPosition(),
            menuHeight = header.getHeight(),
            placeHolder = new Element('div', {'class': 'custom-menu-placeholder'});

        window.addEvent('scroll', function(event) {
            var windowScroll = window.getScroll();

            if (windowScroll.y > headerPosition.y) {
                header.addClass('custom-menu-sticky');
                placeHolder.inject(header, 'after');
                placeHolder.setStyle('height', menuHeight);
            } else {
                header.removeClass('custom-menu-sticky');
                placeHolder.destroy();
            }
        });
    });
},

//jQuery (converted)
setDesktopSticky: function(menuId) {
    var header = jQuery(this).is(menuId) ? menuId : jQuery("#custom-menu")[0];

    jQuery(window).on("load", function() {
        var headerPosition = jQuery(header).position().top,
            menuHeight = jQuery(header).height(),
            placeHolder = jQuery("<div/>", {"class": "custom-menu-placeholder"})[0];

        jQuery(window).scroll(function(event) {
            var windowScroll = jQuery(window).scrollTop();

            if (windowScroll > headerPosition) {
                jQuery(header).addClass("custom-menu-sticky");
                jQuery(placeHolder).appendTo(header);
                jQuery(placeHolder).css("height", menuHeight);
            } else {
                jQuery(header).removeClass("custom-menu-sticky");
                jQuery(placeHolder).remove();
            }
        });
    });
},

And this is the output from each versions, as viewed with the browser inspect tool; the line in question is the one containing ... class="custom-menu-placeholder" ...:

// page inspect for mootools generated output
// ...
▼ <div id="custom-page" class="container"> // [div flashes here when scroll]
    ::before
    ► <div id="custom-header" class="row-fluid">...</div>
    ▼ <div id="custom-menu" class="custom-menu-sticky">
        ► <div id="custom-menu_inner">...</div>
        </div>
        <div class="custom-menu-placeholder" style="height: 54px;"></div>
    ► <div id="custom-body">...</div>
    // etc.

// page inspect for jquery generated output
// ...
▼ <div id="custom-page" class="container">
    ::before
    ► <div id="custom-header" class="row-fluid">...</div>
    ▼ <div id="custom-menu" class="custom-menu-sticky"> // [div flashes here when scroll]
        ▼ <div id="custom-menu_inner">
            ► <div id="custom-menu_inner1">...</div>
            </div>
            <div class="custom-menu-placeholder" style="height: 53px;"></div>
        </div>
    ► <div id="custom-body">...</div>
    // etc.

Other difference is that the browser inspect tool flashes a different div when scrolling (i.e. when web page code changes dynamically), marked above by me with "// [div flashes here when scroll]" (for clarity I didn't mentioned the other flashes that matches same places).

So, question – what is wrong or what should be modified in my converted code in order to obtain the same output on the generated web page ? I don't have jQuery coding experience, so it's very likely that I'm wrong somewhere.


Solution

  • Your "jQuery translation" has a few bits that are either unnecessary or different to what the original mootools code did.

    First off, the line

    var header = jQuery(this).is(menuId) : menuId : jQuery('#custom-menu');
    

    should be the following for a 1:1 match:

    var header = jQuery(menuId ? '#' + menuId : '#custom-menu');
    

    The reason is the same that @CBroe mentioned in the first comment: With the upper line you end up with either a jQuery collection or with a simple String. The lower line addresses that.

    Then, it's completely unnecessary to repeatedly wrap the the same elements into new jQuery collections once you have them. Instead, you can reuse the collections that you already have. Meaning you don't need

    jQuery(header).position().top
    

    because header is a jQuery collection already and you get away with

    header.position().top
    

    instead. In contrast to mootools (which augmented the prototypes of built-in objects and therefor got away with returning the actual object itself), jQuery works with a so-called "jQuery collection" (an array-like structure, it has a .length and stores node via index position but isn't a real Array). Once you wrapped an element/some elements into a jQuery collection, you can refer to that collection for further manipulation.

    Those two things simplify your jQuery code quite a bit:

    setDesktopSticky: function(menuId) {
      var header = jQuery(menuId ? "#" + menuId : "#custom-menu"),
          win = jQuery(window);
    
      win.on("load", function() {
        var headerPosition = header.position().top,
            menuHeight = header.height(),
            placeHolder = jQuery("<div/>", {"class": "custom-menu-placeholder"});
    
        win.scroll(function(event) {
          var windowScroll = win.scrollTop();
    
          if (windowScroll > headerPosition) {
            header.addClass("custom-menu-sticky");
            placeHolder.appendTo(header).css("height", menuHeight); // <-- Note the chained calls
          } else {
             header.removeClass("custom-menu-sticky");
             placeHolder.remove();
          }
        });
      });
    }
    

    Just by reading it seems the reason why the height set by mootools compared with the one set by jQuery is different because mootools used .getBoundingClientRect() under the hood while jQuery uses Math.max combined with either .clientHeight, .offsetHeight and .scrollHeight. See mootools implementation, line 80 and jQuery implementation, line 37.

    EDIT

    Based on your comment, you don't want to use .appendTo in the line

    placeHolder.appendTo(header).css("height", menuHeight);
    

    and instead use .insertAfter

    placeHolder.insertAfter(header).css("height", menuHeight);
    

    The former one adds the placeholder element as the last child inside the header while the latter one adds it after the header.