Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Аналог toggle в javascript? (https://javascript.ru/forum/events/23834-analog-toggle-v-javascript.html)

Dudo4nick 08.12.2011 11:51

Аналог toggle в javascript?
 
А есть аналог toggle в javascript? Ну чтобы по первому клику на некий элемент срабатывала одна функция, а по повторному - совершенно другая? И дальше чередование.

ksa 08.12.2011 13:44

Цитата:

Сообщение от Dudo4nick
А есть аналог toggle в javascript?

А содержимое toggle() это уже не javascript? :D

melky 08.12.2011 15:00

вот написал маленький пример того, как это можно сделать.
сначала решил проблему через свои свойства (element.i), но потом вспомнил, что это плохо и чревато утечкой памяти к некоторых браузерах ;) и решил задачу через имитацию jQuery.data
/*
*  toggle : при срабатывании события будет сначала исполнять первую функцию,
* потому вторую и т.д по порядку
*
* Аргументы :
*   element - элемент, который будет реагировать на события
*   event - тип события.(без 'on')
*   funcArr - массив функций
*/
function toggle(element, event, funcArr){
    // проводим нужную инициализацию

    // мини база данных для элементов, их событий и индексов.
    var BD = [];

    // имеем ли дело с ie
    var ie = /*@cc_on!@*/0;
    
    //исполняет нужную ф-ю из массива
    function handler(e){
        // находим наш элемент
        var element = ie ? window.event.srcElement:e.target;
        
        // ищем сохраненный индекс и извлекаем инфу для этого эл-а из миниБД
        var index = element.getAttribute('id-toggle');
        var elBD = BD[index];
        
        // проверяем наличие исполняем ф-ю из списка с текущим индексом.
        if(elBD.i == elBD.arr.length){ // вышли за массив. начинаем с начала
            elBD.i = 0;
        } 
        elBD.arr[elBD.i].call(element, ie ? event:e);//исполняем ф.ю
        elBD.i += 1; // увеличиваем индекс
    }

    // переназначаем ф-ю
    toggle = function(element, event, funcArr){
        var elBD = {};

        // индекс функции, которая исполнится. 
        elBD.i = 0;
        // массив функций для обхода
        elBD.arr = funcArr;

        var BDindex = BD.push(elBD) - 1; // возвратит новую длину. минус 1 - последний член массива.

        // сохраняем индекс для связки с базой
        element.setAttribute("id-toggle", BDindex);

        // назначаем эл-у обработчик по-умолчанию.
        if(ie){
            element.attachEvent(event, handler);
        } else {
            element.addEventListener(event, handler, false);
        }
    }

    // вызываем изменённую функцию
    toggle(element, event, funcArr);
}

писал на одном дыхании!! потом проверил код - и о чудо, он работает!

пример использования : jsfiddle

и ещё тут :
<script>
function toggle(h,c,i){function f(b){var d=e?event.srcElement:b.target,a=d.getAttribute("id-toggle"),a=g[a];if(a.i==a.arr.length)a.i=0;a.arr[a.i].call(d,e?c:b);a.i+=1}debugger;var g=[],e=0;toggle=function(b,d,a){var c={i:0};c.arr=a;a=g.push(c)-1;b.setAttribute("id-toggle",a);e?b.attachEvent(d,f):b.addEventListener(d,f,!1)};toggle(h,c,i)};
</script>
<div id="a" style="padding:10px;border:2px black solid;border-color:white;">kkjjhkjhlkjh</div>
<script>
toggle(document.getElementById("a"),"click",[function(){this.style.borderColor="red"},function(){this.style.borderColor="green"}]);
</script>

devote 08.12.2011 17:55

Цитата:

Сообщение от melky
var ie = /*@cc_on!@*/0;

а на последних версиях ИЕ это раздве не сработает? Ведь вроде как с ИЕ9 уже работает addEventListener

melky 08.12.2011 22:15

по идее, оно сработает на всех ie ..

devote 08.12.2011 23:09

Цитата:

Сообщение от melky (Сообщение 141476)
по идее, оно сработает на всех ie ..

Ну дык и зачем это юзать, не проще сделать?
if ( document.addEventListener ) {}

devote 08.12.2011 23:12

Цитата:

Сообщение от melky
var element = ie ? e.srcElement:e.target;

И это у тебя работать не будет, надо:
var element = e ? e.target : window.event.srcElement;
Да и других там пару строк надо поправить

melky 08.12.2011 23:40

Цитата:

Сообщение от devote (Сообщение 141489)
Ну дык и зачем это юзать, не проще сделать?
if ( document.addEventListener ) {}

наверное, в связи с ie 9 надо будет перейти на такую конструкцию.

... а e.target в 9 ие работает? если так, то можно будет сделать маленькую помарочку

Код:

для ие < 9 нет событийной модели 2, поэтому будет использоваться attachEvent и window.event.srcElement.
Цитата:

Сообщение от devote (Сообщение 141490)
И это у тебя работать не будет, надо:
var element = e ? e.target : window.event.srcElement;
Да и других там пару строк надо поправить

поправил. я вспомнил про этот косяк, когда уже вышел из дома (код писал перед уходом).

пара-тройка строк для фиксации ивента.. нафиг надо, я не ставил пред собой цель написать всё разом.

x-yuri 03.05.2012 19:29

а вы в курсе, что в ie 10 собираются отменить conditional comments? Правда непонятно, касается ли это jscript. Ну и к слову, target должно работать в ie 9.

melky 03.05.2012 20:03

Цитата:

Сообщение от x-yuri (Сообщение 172573)
а вы в курсе, что в ie 10 собираются отменить conditional comments? Правда непонятно, касается ли это jscript. Ну и к слову, target должно работать в ie 9.

в коде в переменной ie будет true в ie<9 (если не ошибаюсь) - а сама она написана для использования ie api. в ie > 8 она будет false, а в них уже поддеживаются методы w3c.

всё схвачено :)

bes 04.05.2012 10:08

Не проще ли просто использовать некоторую внешнюю переменную как флаг, хранящий одно из пары значений (например, true/false или 0/1), если одно значение флага - исполняй одну функцию, второе - вторую, а при кликах просто изменять значение этого флага.

bes 04.05.2012 10:20

Например, так
<script>
var a = 0;
function toggle() {
if (a == 0) 
  {f1(); a = 1} 
else 
  {f2(); a = 0}
}
</script>

melky 04.05.2012 10:30

Цитата:

Сообщение от bes (Сообщение 172624)
Не проще ли просто использовать некоторую внешнюю переменную как флаг, хранящий одно из пары значений (например, true/false или 0/1), если одно значение флага - исполняй одну функцию, второе - вторую, а при кликах просто изменять значение этого флага.

Цитата:

Сообщение от bes (Сообщение 172625)
Например, так
<script>
var a = 0;
function toggle() {
if (a == 0) 
  {f1(); a = 1} 
else 
  {f2(); a = 0}
}
</script>

в вышеупомянутой функции сделано без замыкания, и с возможностью последовательного (через клик) исполнения хоть тысячи функций.

bes 04.05.2012 11:55

Цитата:

Сообщение от melky
в вышеупомянутой функции сделано без замыкания, и с возможностью последовательного (через клик) исполнения хоть тысячи функций.

Я и не критиковал ваш вариант решения, но в частном случае с двумя чередующимися функциями наверное проще сделать именно так.

melky 04.05.2012 12:08

Цитата:

Сообщение от bes (Сообщение 172633)
Я и не критиковал ваш вариант решения, но в частном случае с двумя чередующимися функциями наверное проще сделать именно так.

замыкания потребляют память. разница в [[scope]] (в случае замыкания) и в объекте (мой случай) небольшая, но есть.сейчас не то время, когда нужно экономить память, но всё-таки лучше придерживаться сильных сторон JavaScript, и не использовать его "источники огромной силы" в каждом скрипте (javascript:Сильные стороны, by Дуглас Крокфорд)

bes 04.05.2012 12:26

Цитата:

Сообщение от melky
замыкания потребляют память

Может я чего-то и не понимаю, но причём здесь вообще замыкания, здесь переменная создаётся не как локальная переменная функции toggle(), а как глобальная переменная, то есть эта переменная была, существует и будет существовать в одном своём экземпляре.

melky 04.05.2012 13:01

Цитата:

Сообщение от bes (Сообщение 172639)
Может я чего-то и не понимаю, но причём здесь вообще замыкания, здесь переменная создаётся не как локальная переменная функции toggle(), а как глобальная переменная, то есть эта переменная была, существует и будет существовать в одном своём экземпляре.

а, да. извиняюсь, просмотрел :) для одного-двух обработчиков так можно сделать, согласен. а что если таких тогглеров около 20 ?

bes 04.05.2012 13:45

Цитата:

Сообщение от melky
для одного-двух обработчиков так можно сделать, согласен. а что если таких тогглеров около 20

Тогда подойдёт оператор switch, а переменную-флаг увеличивать каждый раз на единицу (кроме самого последнего значения, которое надо обнулить).

bes 04.05.2012 14:34

//например, для 3 функций
var a = 0;
function toggle3() {
  switch (a) {
    case 0: {f1(); break}
    case 1: {f2(); break}
    case 2: {f3(); break}
  }
  if (a == 2) a = 0; else a = a + 1;
}


//или так
var a = 0;
function toggle3() {
  switch (a) {
    case 0: {f1(); break}
    case 1: {f2(); break}
    case 2: {f3(); break}
    default: {f1(); a = 0}
  }
  a = a + 1;
}

x-yuri 05.05.2012 23:14

Цитата:

Сообщение от melky
в коде в переменной ie будет true в ie<9 (если не ошибаюсь) - а сама она написана для использования ie api. в ie > 8 она будет false, а в них уже поддеживаются методы w3c.

всё схвачено

В ie <= 9, а по поводу 10-ой версии не совсем понятно. Но не то чтобы это так важно. Я просто недавно узнал об этом изменении, а потом на твой код наткнулся...

А вообще можно так

melky 05.05.2012 23:18

Цитата:

Сообщение от x-yuri (Сообщение 172898)
В ie <= 9, а по поводу 10-ой версии не совсем понятно. Но не то чтобы это так важно. Я просто недавно узнал об этом изменении, а потом на твой код наткнулся...

А вообще можно так

конечно.

я уже давно пишу через feature detection. мой пост какого времени написан :)


Часовой пояс GMT +3, время: 01:19.