Search code examples
javascriptjquerypagespeed

Converting Jquery to Vanilla JS - floating elements


    //First function -> Makes a heading float when scrolled past it
    if (window.matchMedia('(min-width: 767px)').matches) {
    $(function(){
var topBlockheight=$('.site-header').height();
        // Check the initial Position of the fixed_nav_container
        var stickyHeaderTop = $('.float-h2').offset().top;
        var stopFloat = $('#stop-float').offset().top;
        $(window).scroll(function(){
                if( (  $(window).scrollTop() > stickyHeaderTop-topBlockheight && $(window).scrollTop() < stopFloat-topBlockheight )) {
                        $('.float-h2').css({position: 'fixed', top: '200px'});  
                }
                else {
                        $('.float-h2').css({position: 'relative', top: '0px'});
                }
        });
  });
}

// Adds Hover effect for boxes
if (window.matchMedia('(min-width: 767px)').matches) {
$(document).ready(function(){
$(".thumb-cta").mouseover(function(){
    max_value=4;
    random_value= Math.floor((Math.random() * max_value) + 1);
    $(this).attr("data-random",random_value);
});
})
}

These are my two only functions in jQuery from my site which i decided to try to rewrite in vanilla JS. The reason for this decision is because I dont want a 90kb file (jquery main file) to be loaded for 2 basic functions (basic but still can't do them, yes I know).

I've tryed to re write them using http://youmightnotneedjquery.com and https://www.workversatile.com/jquery-to-javascript-converter and i ended up with this code, which does not have any errors in the console, but still does not work :((

            let el = document.getElementById("masthead");
            let topBlockheight = parseFloat(getComputedStyle(el, null).height.replace("px", ""))

            var rect = document.getElementById("float-h2").getBoundingClientRect();

            var offset = { 
                            top: rect.top + window.scrollY, 
                            left: rect.left + window.scrollX, 
                        };


                        var brect = document.getElementById("stop-float").getBoundingClientRect();

            var boffset = { 
                            top: rect.top + window.scrollY, 
                            left: rect.left + window.scrollX, 
                        };

                    window.scroll(function(){
                            if( (  window.scrollTop() > rect-topBlockheight && window.scrollTop() < stopFloat-topBlockheight )) {
                                    document.getElementById("float-h2").css({position: 'fixed', top: '200px'});  
                            }
                            else {
                                    document.getElementById("float-h2").css({position: 'relative', top: '0px'});
                            }
                    });

Any ideas how I can move on, because I'm really stuck


Solution

    • hope this work for you
    if (window.matchMedia('(min-width: 767px)').matches) {
          //Note: if "site-header" is more than one element remove [0] and create a loop
             var topBlockheight=document.getElementsByClassName('site-header')[0].offsetHeight
            var stickyHeaderTop = document.getElementsByClassName('float-h2')[0].offsetTop;
            var stopFloat = document.getElementById('stop-float').offsetTop;
            window.addEventListener("scroll", function(){
                    if( (  window.scrollY > stickyHeaderTop-topBlockheight && window.scrollY < stopFloat-topBlockheight )) {
                            document.getElementsByClassName('float-h2')[0].style.position = 'fixed'
                      document.getElementsByClassName('float-h2')[0].style.top = '200px'
                    }
                    else {
                            document.getElementsByClassName('float-h2')[0].style.position = 'relative'
                      document.getElementsByClassName('float-h2')[0].style.top = 0
                    }
            });
    }
    
    // Adds Hover effect for boxes
    if (window.matchMedia('(min-width: 767px)').matches) {
    document.onreadystatechange = function(){
    if(document.readyState === "interactive") {
         document.getElementsByClassName('thumb-cta')[0].addEventListener("mouseover", function(){
        max_value=4;
        random_value= Math.floor((Math.random() * max_value) + 1);
          this.setAttribute("data-random",random_value);
        });
      }
     }
    }
    

    Details

    1. jQuery
    • in jQuery to select elements by className or tagName it's enough to write $('.element') or $('tag') but in Vanillajs to select elements you can use document.getElementsByClassName('elements') or document.getElementsByTagName('elements') which return found elements sharing the same className or tagName in an array if you want to select only one element you can write the element index like document.getElementsByClassName('elements')[0] but if you want to select all elements you will be need to create a loop
    var el = document.getElementsByClassName('elements')
    for(var i = 0; i < el.length; i++) {
      el[i].style.padding = '25px';
      el[i].style.background = 'red';
    }
    

    and because of id is unque name for the element you don't need to any extra steps you can select it diectly select it like document.getElementById('id')

    that was about selecting elements

    • height() method which uses get the element height - in Vanillajs js to get the element height you can use offsetHeight property or getComputedStyle(element, pseudo|null).cssProp

    Example element.offsetHeight, parseInt(getComputedStyle(element, null).height)

    • offset() method which uses to return coordinates of the element this method have 2 properties top, left in Vanillajs to get the element offset top you can use offsetTop propery directly like element.offsetTop

    • jQuery provides a prototype method like css() which provide an easy and readable way to styling elements like in normal object propery: value - in Vanillajs to styling elements you will be need to use style object like element.style.prop = 'value' and you will be need to repeat this line every time you add new css property like

    el.style.padding = '25px';
    el.style.background = 'red';
    //and they will repeated as long as you add new property
    

    if you don't want to include jQuery into your project and need to use this method you can define it as prototype method for HTMLElement, HTMLMediaElement

    Example 1: for styling one element

    //for html elements
    HTMLElement.prototype.css = function(obj) {
      for(i in obj) {
        this.style[i] = obj[i]
      }
    }
    //for media elements like video, audio
    HTMLMediaElement.prototype.css = function(obj) {
      for(i in obj) {
        this.style[i] = obj[i]
      }
    }
    //Usage
    var el = document.getElementsByClassName('elements')[0]
    el.css({
      'padding': '25px',
      'background-color': 
    })
    

    if you wants to add this style for multiple elements you can define it as prototype method for Array

    Example 2: for multiple elements

    Array.prototype.css = function(obj) {
      for(i = 0; i < this.length; i++) {
        if (this[i] instanceof HTMLElement) {
          for(r in obj) {
            this[i].style[r] = obj[r]
          }
        }
      }
    }
    //Usage
    var el1 = document.getElementsByClassName('elements')[0]
    var el2 = document.getElementsByClassName('elements')[1]
    [el1, el2].css({
      'background-color': 'red',
      padding: '25px'
    })
    
    • jQuery allow you to add events directly when selecting element like $('.element').click(callback) but in Vanillajs you can add events with addEventListener() or onevent proprty like document.getElementById('id').addEventListener('click', callback) , document.getElementById('id').onclick = callback

    • $(document).ready(callback) this method uses to make your code start working after loading the libraries, other things it's useful to give the lib enough time to loaded to avoid errors - in Vanilla js you can use onreadystatechange event and document.readyState protpety which have 3 values loading, interactive, complete

    Example

    document.onreadystatechange = function () {
      if (document.readyState === 'interactive') {
        //your code here
      }
    }
    
    1. Your Coverted Code
    • at this line parseFloat(getComputedStyle(el, null).height.replace("px", "")) you don't need to replace any thing because parseInt(), parseFloat will ignore it
    • element.css() id don't know how you don't get any errors in the console when this method is undefined you will be need to define it as above to make it work
    • scroll event is not defined you will be need to use window.onscroll = callback like example above