简体   繁体   中英

jquery: fadeOut().empty().append(…).fadeIn() doesn't work properly only the first time

I'm struggling to understand why I'm getting the behaviour that I'm seeing. I have a piece of code that aims to fadeOut a container, replace the contents and then fade it back in again when it's done.

I'm using jQuery, so the code looks like this:

var transitionToNewContent = function(container, new_content) {
    container.fadeOut().delay(1000).empty().append(new_content).fadeIn();
};


transitionToNewContent($('#id'), "<p>magic</p>");

The first time that the link that activates this transition is clicked, the content is replaced instantly, then the fadeout happens, then it fades in again.

Every time after that when the link is clicked, I'm seeing the correct behaviour: fadeout, then fadein with the new content.

Any idea why this is happening?

I've attached a complete html file which shows the behaviour:

I'm fairly new to jquery and I'm trying to do things 'the right way'. Any comments regarding style will be appreciated.

<doctype html>
<html>
<head>
  <style type="text/css">
    #main_container {
    width: 800px;
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 3em;
    margin-top: 0;
    height: 100%;
    position: relative;
    top: 0px;
    bottom: 0px;
}

#loading {
    float: right;
    position: relative;
    top: 10px;
    left: 10px;
    z-index: 1;
}

#sidebar {
    float: left;
    width: 240px;
    border: #078600 1px dashed;
    position: relative;
    top: 10px;
    left: -250px;
    margin-right: 20px;
    background: #cccccc;
    z-index: 1;
    padding: 10px;
    font-size: 0.65em;
    display: none;
}

#main_content {
    z-index: 0;
    position: absolute;
    top: 0px;
    left: 5px;
    width: 100%;
    height: 100%;
    float: right;
    background: white;
    padding-top: 0;
    padding-bottom: 3em;
    padding-left: 20px;
    border-right: 1px dotted #078600;
    border-left: 1px dotted #078600;
    border-top: 3px solid white;
}

  </style>
  <link rel="stylesheet" type="text/css" href="/main.css" />
</head>
<body>
  <div id="main_container">
    <div id="sidebar"><h1>Sidebar</h1><p>some text</p></div>
    <div id="loading" style="display: none">&nbps;</div>

    <div id="main_content">
      <h1 id="page_title">Title</h1>
      <div id="page_content"><h2>heading</h2><p>Some testing text</p></div>
    </div>
  </div>
  <script type="text/javascript">
var include = function(src) {
    document.write('<script type="text/javascript" src="' + src + '"></scr' + 'ipt>'); 
};

include("http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js");
include("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js");

var bootStrapSites = function() {
    var sideBar = $('#sidebar');
    sideBar.delay(1000).fadeIn();
    loadSiteList(sideBar);
};

var transitionToNewContent = function(container, new_content) {
    container.fadeOut().delay(1000).empty().append(new_content).fadeIn();
};

var editSite = function(site) {
    transitionToNewContent($('#page_content'), "<p>This is where the magic will happen</p>");
};

var loadSiteList = function(container) {
//    $.getJSON("/sites/list.json", function(data) {

            var data = $.parseJSON('[{"id": "1", "name": "cool name"}]');
        container.empty(container);
        container.append("<h2>new heading</h2>");
        container.append("<ul id='sitesList'></ul>");

        var sitesList = $('#sitesList');
        $.each(data, function(idx, site) {
            var id = 'show_site_id_' + site._id;
            sitesList.append("<li><a id='" + id + "' href='#'>" + site.name + "</a></li>");
            $('#' + id).click(function() {
                editSite(site);
            });
        });
//  });
};
</script>
  <script type="text/javascript">

$(document).ready(function() {
   bootStrapSites();
});

  </script>
</body>
</html>

The problem is that there are two different types of functions in jQuery -- those that take effect immediately and those that add effects to the effects queue.

Let's take a look at your code:

container.fadeOut().delay(1000).empty().append(new_content).fadeIn();
  • fadeOut adds a fade operation to the queue
  • delay adds a delay to the queue
  • empty immediately empties the element
  • append immediately appends the content
  • fadeIn adds a fade operation to the queue

The functions that add items to the queue return immediately -- they don't wait to complete before taking effect. This means that all the functions are called at (pretty much) the same time -- they don't wait for the animation functions to complete.

You need to use the callback argument to fadeOut instead:

container.fadeOut(1000, function(){
    container.empty().append(new_content).fadeIn();
});

The function is triggered when the fade operation is complete, so the emptying and appending are delayed until the element is hidden.

You need to use a callback function to get the effect you are looking for.

var transitionToNewContent = function(container, new_content) {
    container.fadeOut(1000, function(){
         $(this).empty().append(new_content).fadeIn();
    });
};

This ensures that the container isn't replaced until AFTER the animation is completed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM