jQuery - 嵌套与未通过的转换和动画事件处理程序/侦听器

时间:2016-08-28 02:26:54

标签: javascript jquery html css

我目前正在处理一组代码,每次发生某个ajax调用时都会运行。

为了做到这一点,我使用下面的代码片段(因为我认为没有html或css,所以错误,但是我只是想显示我的代码,这是正常的):

$(document).ajaxStart(function() {
  if ($('.spinner').length < 1) { //as there is no ajaxStop, this makes sure there is not already a spinner
    $('body').prepend('<div class="spinner" title="Spinner stuck? Click to remove!"></div>');
  }
  $('body div').not('.spinner').css('opacity', '0.5'); //this is potentially unnecessary
});

//the rest of the ajax call is not included - just the .done()
.done(function(xhr) {
  $('body div').not('.spinner').css('opacity', '0');
  $('body').one('transitionend', function(e) {
    $('body div').not('.spinner').remove();
    $('body').append(xhr);
    $('body div').not('.spinner').css('opacity', '0.1');
    $('body').one('transitionend', function(e) {
      $('body div').css('opacity', '1');
      $('.spinner').remove();
    });
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

正如您所看到的,我嵌套了.one('transitionend'),我试图弄清楚它是否正确(作为注释:该代码用于处理登录和注销ajax调用对于我的登录ajax调用,它大部分时间工作但是当前没有用于注销ajax调用 - 我认为它没有注册结束$('body div').not('.spinner').css('opacity', '0.1');的转换,因为这就是冻结的地方。)

我把this jsfiddle放在了一起。它只是测试嵌套与未声明的事件处理程序。

它们(嵌套和无效)似乎工作原理相同(如果你点击其中一个太多次就会冻结它们 - 它发生在嵌套和未嵌入的情况下 - 但这似乎是在我添加了记录位 - $('#log span').text($('#div1').text());等。)。

以下是片段中的jsfiddle:

var newCss = {
  backgroundColor: 'blue',
  width: '25%',
  color: 'white'
};
var disabled = false;
$(document).on('click', '#div1', function(e) {
  if (disabled) return;
  notNested($(this));
});

$(document).on('click', '#div8', function(e) {
  if (disabled) return;
  nested($(this));
});

function notNested($this) {
  disabled = true;
  $this.css(newCss);
  $('#log span').text($('#div1').text());
  $('#div1').one('transitionend', function() {
    $('#div2').css(newCss);
    $('#log span').text($('#div2').text());
  });
  $('#div2').one('transitionend', function() {
    $('#div3').css(newCss);
    $('#log span').text($('#div3').text());
  });
  $('#div3').one('transitionend', function() {
    $('#div4').css(newCss);
    $('#log span').text($('#div4').text());
  });
  $('#div4').one('transitionend', function() {
    $('#div5').css(newCss);
    $('#log span').text($('#div5').text());
  });
  $('#div5').one('transitionend', function() {
    $('#div6').css(newCss);
    $('#log span').text($('#div6').text());
  });
  $('#div6').one('transitionend', function() {
    $('#div7').css(newCss);
    $('#log span').text($('#div7').text());
  });
  $('#div7').one('transitionend', function() {
    $('div').removeAttr('style');
    $('#log span').text('');
    disabled = false;
  });
}

function nested($this) {
  disabled = true;
  $this.css(newCss);
  $('#log span').text($('#div8').text());
  $('#div8').one('transitionend', function() {
    $('#div9').css(newCss);
    $('#log span').text($('#div9').text());
    $('#div9').one('transitionend', function() {
      $('#div10').css(newCss);
      $('#log span').text($('#div10').text());
      $('#div10').one('transitionend', function() {
        $('#div11').css(newCss);
        $('#log span').text($('#div11').text());
        $('#div11').one('transitionend', function() {
          $('#div12').css(newCss);
          $('#log span').text($('#div12').text());
          $('#div12').one('transitionend', function() {
            $('#div13').css(newCss);
            $('#log span').text($('#div13').text());
            $('#div13').one('transitionend', function() {
              $('#div14').css(newCss);
              $('#log span').text($('#div14').text());
              $('#div14').one('transitionend', function() {
                $('#log span').text('');
                $('div').removeAttr('style');
                disabled = false;
              });
            });
          });
        });
      });
    });
  });
}
body {
  display: flex;
  align-items: flex-start;
  /* Can't remember if flex-start is default anyways*/
}
div {
  display: inline-block;
  width: 25%;
}
div > div {
  border: 4px dotted red;
  transition: .25s;
  display: block;
  width: 10%;
  padding: 4px 8px;
  margin: 16px 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div>
  <div id="div1">1</div>
  <div id="div2">2</div>
  <div id="div3">3</div>
  <div id="div4">4</div>
  <div id="div5">5</div>
  <div id="div6">6</div>
  <div id="div7">7</div>
</div>
<div id="log">We got to: <span></span>
</div>
<div>
  <div id="div8">8</div>
  <div id="div9">9</div>
  <div id="div10">10</div>
  <div id="div11">11</div>
  <div id="div12">12</div>
  <div id="div13">13</div>
  <div id="div14">14</div>
</div>

  

因此,我试图找出哪一个更好用?这对代码/浏览器造成的问题较少?等等。是否有更好的方法来控制依赖于转换(或动画的动作)处于特定状态的代码? (寻找事实答案,而不是意见, TUVM

我曾尝试过研究jQuery的.queue.delay,但无法在我的场景中工作(可能是我使用它们错了)。也许这是另一种可能性?我也知道setTimeout()是可能的,但要保持同步,你需要在它之后嵌套元素(并且.one('transitionend')似乎在大多数情况下工作 无论你是否嵌套......)

UPDATE:

根据此问题留下的以下评论:

  

关于代码的一些小指针,不要在Jquery中执行css,而是使用不透明度值追加/前置类。使用变量而不是使用每个选择器遍历DOM。 - Jan_dh

我更新了jsfiddle以更改类而不是直接更改CSS。 Here是链接(同样,上面的jsfiddle链接已更新)。

虽然,我不重复的意思是:

  

使用变量而不是通过每个选择器遍历DOM。

如果这可能是我的问题的答案,也许有人明白他的意思可以在下面阐述?

1 个答案:

答案 0 :(得分:2)

您可以使用.queue()$.map(); .one("transitionend")附加到当前转换元素,处理程序设置为next,以便在当前元素transition完成时调用队列中的下一个函数;单个事件处理程序,用于.slice()将元素id"div"开头的所有元素拆分为数组的两个或多个元素;将数组作为event.data传递给事件处理程序;使用$.grep().filter()过滤特定列;将元素集合.data(/* name */)设置为truefalse,以防止当转换正在进行时元素当前正在元素click的队列中转换时转换效果的默认操作;当前队列完成时.promise().then()执行任务

&#13;
&#13;
var newCss = {
  backgroundColor: "blue",
  width: "25%",
  color: "white"
}
// define `log` variable as `"#log span"`
, log = $("#log span")
// define `divs` variable as all elements where `id` begins with`` "div"
, divs = $("[id^=div]")
// define `col1` variable as `divs` at indexes `0` through `7`
, col1 = divs.slice(0, 7)
// define `col2` variable as `divs` at indexes `7` through `div.length`
, col2 = divs.slice(7, divs.length);

function queueColumn(col, name) {
  // set current collection of elements `.data("active")` to `true`
  return col.data("active", true)
    // set a queue name with `name`, call `$.map()` on current collection
    .queue(name, $.map(col, function(div) {
     // return a function at queue `name` for each element in collection
      return function(next) {
        // set `log` text
        log.text(
          // pass reference of `next` function in queue `name`
          // as handler for `transitionend` event of current element;
          // call `next` function in queue `name`
          // at `transitionend` event of element
          $(div).one("transitionend", next).css(newCss).text()
        )
      }
    // call first function in queue;
    // when all function in queue `name` complete,
    // return queue `name` jQuery promise object
    })).dequeue(name).promise(name)
}
// handle `click` event at `#div1`, `#div8` elements
function handleQueue(e) {
  // define `curr` variable;
  // filter array containing `col1`, `col2` collections
  // to match collection containing `#div1` or `#div8`     
  var curr = $.grep(e.data, function(col) {
    return col.filter(e.target).length
  }).pop();
  // create `name` for queue
  var name = `col${$.inArray(curr, e.data) + 1}-${$.now()}`;
  // check if `curr` `.data("active")` is `undefined` 
  // at first click of element, if true, 
  // set `curr` `.data("active")` to `false` 
  if (curr.data("active") === undefined) {
    curr.data("active", false);
  }
  if (curr.data("name") === undefined) {
    curr.data("name", name);
  }
  console.log(`queue ${curr.data("name")} is active:`
             , `${!curr.queue(name).length 
                  || curr.data("active")}`);
  // define `inprogress` as `curr.data().active`
  var inprogress = curr.data().active;
  // check if `inprogress` is `false`, if true, call call `queueColumn`
  // with `curr`, `name` as parameters
  // if `#div1` or `#div8` element are clicked during 
  // during queue, while `inprogress` is `true`,
  // return `false` from event handler to prevent
  // multiple queues to be set at `curr` at same time
  return !inprogress
         ? queueColumn(curr, name)
           // do stuff when current queue `name` completes
           // calling all functions in queue array
           .then(function() {
             console.log(`${this.data("name")} queue complete`);
             // remove element `style`, reset `.data()`
             this.removeAttr("style")
             .data({"active": false, "name": void 0});        
            }) 
           // else return `false` 
         : inprogress
}
// at `click` of `#div1`, `#div8`, call `handleQueue`,
// pass array containing `col1`, `col2` as `event.data`
$("#div1, #div8").click([col1, col2], handleQueue);
&#13;
body {
  display: flex;
  align-items: flex-start;
  /* Can't remember if flex-start is default anyways*/
}
div {
  display: inline-block;
  width: 25%;
}
div > div {
  border: 4px dotted red;
  transition: .25s;
  display: block;
  width: 10%;
  padding: 4px 8px;
  margin: 16px 0px;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js">
</script>
<div>
  <div id="div1">1</div>
  <div id="div2">2</div>
  <div id="div3">3</div>
  <div id="div4">4</div>
  <div id="div5">5</div>
  <div id="div6">6</div>
  <div id="div7">7</div>
</div>
<div id="log">We got to: <span></span>
</div>
<div>
  <div id="div8">8</div>
  <div id="div9">9</div>
  <div id="div10">10</div>
  <div id="div11">11</div>
  <div id="div12">12</div>
  <div id="div13">13</div>
  <div id="div14">14</div>
</div>
&#13;
&#13;
&#13;