Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plain javascript trigger css fade out, ajax load content, fade in using CSS

Trying to figure out how to do this with plain javascript and css, below is code working with jquery.

In this example a user clicks a link, the script checks to see if there is content in a div, if there is, it fades out the content, then loads by ajax the new content and fades it in.

I know I can toggle a css class, but wondering how to go about using a callback to see when the css animation has completed to be able to fire the ajax request and then fade in.

<section>
    <a href="#" data-file="data1">Load data 1</a>
    <a href="#" data-file="data2">Load data 2</a>
    <div id="content"></div>
</section>

$(document).ready(function(){
    $('body').on('click','a',function(event){
        event.preventDefault();
        var datafile = $(this).data('file');
        console.log(datafile);
        if($('#content').html().length){
            $('#content').fadeOut(700,function(){
                $('#content').load(datafile + '.php').hide().fadeIn(700);
                console.log('has content, fadeout then load fadein ' + datafile);
            })
        } else {
            $('#content').load(datafile + '.php').hide().fadeIn(700);
            console.log('no content, load fadein ' + datafile);
        }
    });
});

contents of data1.php and data2.php are filled with lorem ipsum just for testing purposes, in production they would be interface screens for a cms.

here is a jsfiddle https://jsfiddle.net/nomadwebdesign/cfvd6uk4/

Looking at Dan Dascalescu's answer and how to expand upon this How to do fade-in and fade-out with JavaScript and CSS

I've also been trying to use the on('transitionend') event listener, however it goes into a loop because after loading by ajax I remove the css class causing the transition again.

I am able to do this by using setTimeout and matching the transition duration however this seems flaky.

At this point I would just like to know how to do this with jQuery and CSS without using fadeIn() and fadeOut()

the sequence is click -> fade out previous content -> fade in ajax loaded content

UPDATE Here is a working solution using jQuery without fadeIn() or fadeOut() but with CSS opacity transition

<section id="section2">
    <a href="#" data-file="data1">Load data 1</a>
    <a href="#" data-file="data2">Load data 2</a>
    <div id="dataContent"></div>
</section>

$(document).ready(function(){
    $('body').on('click','#section2 a',function(event){
        event.preventDefault();
        var datafile = $(this).data('file');
        var dataContent = $('#dataContent');
        // if there is content fade it out first
        if(dataContent.html().length){
            dataContent.addClass('opa fade');
            // check for completed transition
            dataContent.one('transitionend', function(){
                dataContent.load(datafile + '.php', function(){
                    dataContent.removeClass('fade');
                });
            }); 
        } else {
            // if there is no content just load and fade in
            dataContent.addClass('fade');
            dataContent.load(datafile + '.php', function(){
                dataContent.removeClass('fade');
                dataContent.addClass('opa');
            });
        }
    });
});

#dataContent.opa {
    opacity: 1;
    transition: opacity .700s; 
}
#dataContent.fade {
  opacity: 0;
}

Still looking for a solution to this without jQuery using vanilla javaScript

referencing these posts for help

https://blog.teamtreehouse.com/using-jquery-to-detect-when-css3-animations-and-transitions-end

https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/

After spending all day thoughtfully researching this and pulling everything I could find from the web together I have a working solution. This is an example using plain vanilla JavaScript Ajax and CSS3 transitions. I have found this to be a popular trend in the last few years (2017-2019) as browsers have advanced an older browsers have fallen away. Also, with the dependency upon jQuery less and less it seems more people are looking for plain js versions and leveraging css3 transitions and animations. I believe this question and answer to be of more detail than the others linked here on stackoverflow and believe this example will be of use to many others.

Here is the working solution

css
#dataContent.opa {
    opacity: 1;
    transition: opacity .400s; 
}
#dataContent.fade {
    opacity: 0;
}
html
<section id="section">
    <a href="#" class="clicker" data-file="data1">Load data 1</a>
    <a href="#" class="clicker" data-file="data2">Load data 2</a>
    <a href="#" class="clicker" data-file="data3">Load data 3</a>
    <div id="dataContent"></div>
</section>
var classname = document.getElementsByClassName('clicker');
ajaxf = function(event){
    var xhttp = new XMLHttpRequest();
    event.preventDefault();
    var datafile = this.getAttribute('data-file');
    var dataContent = document.getElementById('dataContent');
    if(dataContent.innerHTML.length){
        dataContent.classList.add('opa','fade');
        dataContent.addEventListener('transitionend',handler);
        function handler(event) {
            event.target.removeEventListener(event.type,arguments.callee);
            xhttp.onreadystatechange = function(){
                if(this.readyState == 4 && this.status == 200){
                    dataContent.innerHTML = this.responseText;
                    dataContent.classList.remove('fade');
                };
                if(this.status == 404){
                    dataContent.classList.remove('fade');
                    dataContent.innerHTML = 'there was an error retrieving data';
                }               
            };
            xhttp.open('GET',datafile + '.php',true);
            xhttp.send();
        }
    } else {
        dataContent.classList.add('fade');
        xhttp.onreadystatechange = function(){
            if(this.readyState == 4 && this.status == 200){
                dataContent.innerHTML = this.responseText;
                dataContent.classList.remove('fade');
                dataContent.classList.add('opa');
            };
            if(this.status == 404){
                dataContent.innerHTML = 'there was an error retrieving data';
                dataContent.classList.remove('fade');
                dataContent.classList.add('opa');
            }
        };
        xhttp.open('GET',datafile + '.php',true);
        xhttp.send();
    }
};
for(var i = 0; i < classname.length; i++){
    classname[i].addEventListener('click',ajaxf,false);
};

One of the main keys in this process is creating an event listener for the transitionend and then removing the event listener (to repeat the function when the 2nd transition fires)

enter image description here

like image 435
drooh Avatar asked Oct 27 '19 16:10

drooh


1 Answers

You can use a simpler and modern approach to ajax using fetch API. Also, using a combination of data-* attribute and CSS simplify fading in and out.

Set this example:

var classname = document.getElementsByClassName('clicker');
let dataContent = document.getElementById("dataContent");

/*
 * Manage transtion in and out in sucess and error callback
 */
function transition(content) {
  dataContent.classList.add("fade");

  var clickFunction = function(event) {
    dataContent.innerHTML = content;
    dataContent.classList.remove("fade");
  };

  dataContent.addEventListener("transitionend", clickFunction);

  return true;
}

// Main function, to call fetch
function ajaxf() {
  var datafile = this.getAttribute('data-file');
  var opts = {
    method: 'GET'
  };


  fetch(datafile, opts).then(function(response) {
      /* If we dont get an OK response (200) then through an error to
       trigger catch callback
       */
      if (response.status !== 200)
        throw new Error("Not 200 response")

      /* Parse response to json. You can use text() as well
       *	https://developer.mozilla.org/en-US/docs/Web/API/Body
       */
      return response.json();
    })
    .then(function(content) {
      // check if content has been set previously
      let hasContent = dataContent.getAttribute("data-hascontent");

      if (hasContent != 'false')
        return transition(content.body);

      // Process empty div
      dataContent.innerHTML = content.body;
      dataContent.setAttribute("data-hascontent", "true");

    }).catch(function(error) {
      // Same as previous methid
      let hasContent = dataContent.getAttribute("data-hascontent");

      if (hasContent != 'false')
        return transition(error);

      dataContent.innerHTML = error;
      dataContent.setAttribute("data-hascontent", "true");
    });
}

// Attach Events
for (var i = 0; i < classname.length; i++) {
  classname[i].addEventListener('click', ajaxf, false);
}
#dataContent {
  opacity: 0;
  transition: opacity .400s;
}

#dataContent[data-hascontent='true'] {
  opacity: 1;
}

#dataContent[data-hascontent='true'].fade {
  opacity: 0;
}
<section id="section">
  <a href="#" class="clicker" data-file="https://jsonplaceholder.typicode.com/posts/1">Load data 1</a>
  <a href="#" class="clicker" data-file="data2">Load data 2</a>
  <a href="#" class="clicker" data-file="https://jsonplaceholder.typicode.com/posts/5">Load data 3</a>
  <div id="dataContent" data-hascontent='false'></div>
</section>
like image 108
Kalimah Avatar answered Oct 05 '22 23:10

Kalimah