Подтвердить что ты не робот

Код jQuery слишком многословный, хотел бы указать указатели на то, как сделать его короче

У меня есть код jQuery, на который я бы хотел посмотреть обзоры и указатели на то, как подсчитывать и сокращать количество строк.

$('#p1').click(function() {
    $('#list').fadeOut(450);
    $('#q1').delay(600).fadeIn(450)
});
$('#p2').click(function() {
    $('#list').fadeOut(450);
    $('#q2').delay(600).fadeIn(450)
});
$('#p3').click(function() {
    $('#list').fadeOut(450);
    $('#q3').delay(600).fadeIn(450)
});
$('#p4').click(function() {
    $('#list').fadeOut(450);
    $('#q4').delay(600).fadeIn(450)
});

...

$('#p12').click(function() {
    $('#list').fadeOut(450);
    $('#q12').delay(600).fadeIn(450)
});
$('#p13').click(function() {
    $('#list').fadeOut(450);
    $('#q13').delay(600).fadeIn(450)
});

Можно ли оптимизировать этот код? Или, по крайней мере, сделать менее многословным?

4b9b3361

Ответ 1

Вы можете использовать цикл for, но вы должны убедиться, что значение счетчика циклов попадает в правильную область для обработчика событий click:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};
for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

В противном случае delay и fadeIn будут применяться только к элементу #q13, так как фактический счетчик (с его конечным значением 13) будет закрыт.


РЕДАКТИРОВАТЬ. Поскольку здесь было довольно много ответов, я попытаюсь более точно объяснить, что происходит в этом коде, поскольку это кажется довольно запутанным.

"Естественное" решение с инжектированием обработчика щелчка непосредственно в цикл будет следующим:

for(var i = 1; i < 14; i++) {
    $('#p'+i).click(function() {
        $('#list').fadeOut(450);
        $('#q'+i).delay(600).fadeIn(450)
    });
}

Но это совсем не эквивалентно расширенной форме, в которой перечислены все 13 вариантов один за другим. Проблема в том, что, хотя здесь создано 13 функций, все они закрыты по той же переменной i, значение которой изменяется. Он, наконец, достигает значения 13, и цикл завершается.

Спустя некоторое время вызываются функции, прикрепленные к элементам #p1... #p13 (при нажатии одного из этих элементов), и они используют это окончательное значение i. Это приводит только к анимации #q13.

Что нужно сделать, так это сделать что-то, называемое лямбда-лифтинг, и устранить свободную переменную i, значение которой будет случайно изменено. Общей методикой является предоставление "factory функции", которая принимает значение для нашей переменной и выводит фактическую функцию, которую мы будем использовать в качестве обработчика события:

var clickHandler = function(k) {
    return function() {
        $('#list').fadeOut(450);
        $('#q' + k).delay(600).fadeIn(450);
    };
};

Поскольку область действия параметра k локальна для clickHandler, каждый вызов clickHandler получает другую переменную k. Функция, возвращаемая из clickHandler, поэтому закрыта для разных переменных, которые, в свою очередь, могут иметь разные значения. Это именно то, что нам нужно. Затем мы можем вызывать clickHandler из нашего цикла, передавая ему значение счетчика:

for (var i = 1; i < 14; ++i) {
    $('#p' + i).click(clickHandler(i));
}

Надеюсь, это немного изменит ситуацию.


РЕДАКТИРОВАТЬ. Как отметил Есалия в комментариях, для достижения аналогичного эффекта можно использовать jQuery.each:

$.each(new Array(13), function(idx) {
    $('#p' + (idx + 1)).click(function() {
        $('#list').fadeOut(450);
        $('#q' + idx).delay(600).fadeIn(450);
    });
});

Это, вероятно, решение по выбору, если вы уже знаете о проблеме закрытия/охвата, которую я попытался описать выше.

Ответ 2

Идеальное решение IMO, вопреки принятому ответу, не должно основывать ваш код на отношениях между значениями id, а основывать его на отношениях в DOM. jQuery предоставляет вам метод index, который позволяет вам видеть, где находится ваш элемент по отношению к его братьям и сестрам. Затем вы можете использовать это значение, чтобы выбрать соответствующий другой элемент для показа.

Это, конечно, требует, чтобы вы структурировали свой HTML семантическим способом. Например:

<div id="list">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
    <a href="#">Link 4</a>
    <a href="#">Link 5</a>
    <a href="#">Link 6</a>
    <a href="#">Link 7</a>
    <a href="#">Link 8</a>
</div>
<div id="questions"> <!-- perhaps this is what q stands for... -->
    <div>1 ...</div>
    <div>2 ...</div>
    <div>3 ...</div>
    <div>4 ...</div>
    <div>5 ...</div>
    <div>6 ...</div>
    <div>7 ...</div>
    <div>8 ...</div>
</div>

Первая ссылка относится к первой вложенной div, второй ко второй и т.д. Разметка проста, легко читается и четко семантична.

Ваш код может быть очень простым, без необходимости беспокоиться о фабриках. Действительно, самый лучший способ сделать это - с пузырьками событий и делегированием, отлично обработанными методом jQuery on (в 1.7, перед этим используйте delegate).

$('#list').on('click', 'a', function() {
    $('#list').fadeOut(450);
    $('#questions div').eq($(this).index()).delay(600).fadeIn(450);
});

Работа jsFiddle (я знаю, что анимация выглядит немного неуклюжей, извините.)

$(this).index() находит отношение текущего элемента относительно его братьев и сестер. .eq() фильтрует выбор (элементов #questions div), чтобы найти элемент в соответствующей позиции.

Код может быть не таким быстрым (если вы используете значения id, это почти наверняка будет намного быстрее), но ваша разметка проще и, следовательно, более надежна.

Ответ 3

Если вы должны настаивать на том, чтобы не использовать делегирование событий и классы, выполните следующие действия:

$.each( new Array(13), function(index){
    $('#p'+(index+1) ).click(function() {
        $('#list').fadeOut(450);
        $('#q'+(index+1)).delay(600).fadeIn(450)
    });
});

Использование классов и делегирования:

$( document ).delegate( ".my-class", "click", function(){
var idIndex = this.id.match( /\d+$/, )[0];

        $('#list').fadeOut(450);
            $('#q'+idIndex).delay(600).fadeIn(450)

});

Ответ 4

Как и другие, вы можете использовать for-loop для этого, но на самом деле вы привязываетесь к 14 p, и всякий раз, когда вы добавляете новый p, вам нужно увеличить тестовое условие for-loop.

Это то, что я сделал бы:

$("[id^=p]").bind("click", function() {
    $('#list').fadeOut(450);
    $('#q' + $(this).attr("id").match(/^#p(\d+)$/i)[1]).delay(600).fadeIn(450)
})

Ответ 5

Не могли бы вы просто использовать класс вместо всех этих разных идентификаторов? А затем какую-нибудь навигацию, чтобы найти соответствующий другой элемент?

$('.myCoolEffect').click(function(event) {
    $('#list').fadeOut(450);
    // find the matching element based on this on somehow
    $(event.currentTarget).up(1).delay(600).fadeIn(450);
});

Ответ 6

Попробуйте это,

$('[id^="p"]').live('click', function(e){
    var temp = this.id.match(/^p(\d+)$/);
    if(temp){
       $('#list').fadeOut(450);
       $('#q' + temp[1]).delay(600).fadeIn(450);
    }

});

Я использовал jqueryStartsWith Selector и функция live.

Update:

Как, Xion прокомментировал: если вы используете этот код, это повлияет на все остальные элементы, из которых id's начинаются с p.

Если бы вы могли добавить больше символов в идентификатор, это не будет проблемой.

Обновление 2:

Как, BrunoLM, упомянутых в комментарии, я немного изменил код, чтобы не инициировать событие для других элементов в dom, которые имеют идентификатор начинается с p.

var temp = this.id.match(/^p(\d+)$/); и следующий, если блок позаботится о других элементах, чей id начинается с p.

match(/^p(\d+)$/) возвращает null для id, как pblabla, p2sdad, pdasds2323, и возвращает массив для id, например p1, p2, ... p100000....

Ответ 7

$('div').filter(function () {
  return this.id.match(/^p([0-9]+)$/);
}).click(function () {
  $('#list').fadeOut(450);
  $('#' +$(this).attr('id').replace('p','q')).delay(600).fadeIn(450)
});