Как динамически изменять обработчики событий с помощью JavaScript? - PullRequest
0 голосов
/ 02 января 2011

Я довольно новичок во всем AJAX и столкнулся со следующей проблемой. Я надеюсь, что кто-то здесь сможет мне помочь. Я уверен, что это легко исправить, но я просто не вижу этого: (

Я пытаюсь динамически изменять события onmouseover и onmouseout для элемента, вот соответствующие биты кода:

Элемент HTML (обратите внимание, что их несколько, следовательно, это динамическая часть их идентификатора)

<?
if (ownsgame($id, $userid)) {?>
    <a><img src="images/collection/got.gif" id="ownedimage<?=$id?>" title="<?=$itemtitle?> (<?=$platformdisplay?>) is in your collection" alt="You own this game" align="center" width="62" height="22" onclick="changeOwned('<?=$id?>')" onmouseover="changeImageSrc('ownedimage<?=$id?>', 'images/collection/del.gif')" onmouseout="changeImageSrc('ownedimage<?=$id?>', 'images/collection/got.gif')"/></a>
<? } else { ?>
    <a><img src="images/collection/add.gif" id="ownedimage<?=$id?>" title="Add <?=$itemtitle?> (<?=$platformdisplay?>) to your collection" alt="You do not own this game" align="center" width="62" height="22" onclick="changeOwned('<?=$id?>')" onmouseover="changeImageSrc('ownedimage<?=$id?>', 'images/collection/add.gif')" onmouseout="changeImageSrc('ownedimage<?=$id?>', 'images/collection/add.gif')"/></a>
<?} ?>

Функция JavaScript:

function changeImageSrc(id, src) {
    document.getElementById(id).src = src;
}

(соответствующий) код AJAX:

var http = createRequestObject();
var jsid = "";

function changeOwned(id) {
    http.open('get', 'changeowned.php?id=' + id + '&user=<?=$userid?>');
    jsid = id;
    http.onreadystatechange = processResponse;
    http.send(null);
}

function processResponse() {
if((http.readyState == 4) && (http.status == 200)){

    var response = http.responseText;
    var elementid = 'ownedimage' + jsid;
    var element = document.getElementById(elementid);
    if (response == "1") {
        image = "images/collection/got.gif";
        alt = "you own this game";
        mouseoverstr = 'images/collection/del.gif';
        mouseoutstr = 'images/collection/got.gif';
        element.removeEventListener('mouseout', function(){changeImageSrc('ownedimage' + jsid, 'images/collection/add.gif')}, false);
        element.removeEventListener('mouseover', function(){changeImageSrc('ownedimage' + jsid, 'images/collection/add.gif')}, false);
    } else {
        image = "images/collection/add.gif";
        alt = "add this game to your collection";
        mouseoverstr = 'images/collection/add.gif';
        mouseoutstr = 'images/collection/add.gif';
        element.removeEventListener('mouseout', function(){changeImageSrc('ownedimage' + jsid, 'images/collection/got.gif')}, false);
        element.removeEventListener('mouseover', function(){changeImageSrc('ownedimage' + jsid, 'images/collection/del.gif')}, false);
    }
    element.src = image;
    element.alt = alt;
    element.addEventListener('mouseover', function(){changeImageSrc('ownedimage' + jsid, mouseoverstr)}, false);
    element.addEventListener('mouseout', function(){changeImageSrc('ownedimage' + jsid, mouseoutstr)}, false);
}
}

Поначалу кажется, что работает нормально, но вызывает странное поведение. Указанный PHP работает нормально и выдает правильный ответ. Srcand Alt изображения также изменились. На самом деле, сначала это выглядит как новая работа при наведении курсора мыши. Но когда вы нажимаете на несколько изображений на сайте (которые имеют разные идентификаторы), они внезапно начинают влиять друг на друга. Когда вы наводите курсор мыши над одним, другой тоже меняет свое изображение. Почему это происходит? Я действительно ничего не понимаю, так как с jsid все в порядке, и я не понимаю, почему при наведении мыши внезапно меняются два изображения. Выглядит так, как будто несколько обработчиков событий для разных идентификаторов назначены одному и тому же элементу изображения. Не знаю, почему это так. Я надеюсь, что некоторые из вас, обладающие большим знанием AJAX, могут помочь мне в этом, очень расстроенные: (

Ответы [ 2 ]

2 голосов
/ 02 января 2011

Проблема в removeEventListener, она не работает так, как вы думаете.Когда вы делаете это:

element.removeEventListener('mouseout',function(){/* handlers */},false);

То, что function() { } является новой анонимной функцией, которую вы только что создали, а не существующей в элементе в качестве прослушивателя ...когда он удаляет этого слушателя, его просто нет, потому что это была другая анонимная функция (даже если у нее был точно такой же код, это другая ссылка), котораяВы назначены через addEventListener.

Для этого есть несколько обходных путей, учитывая, что вы делаете запросы AJAX. Я предполагаю, что вы не делаете здесь сотни / тысячи, так что вы можете просто сохранитьобработчики, которые вы назначите для их удаления позже, например:

var http = createRequestObject();
var jsid = "";
var handlers = {};

function changeOwned(id) {
    http.open('get', 'changeowned.php?id=' + id + '&user=<?=$userid?>');
    jsid = id;
    http.onreadystatechange = processResponse;
    http.send(null);
}

function processResponse() {
  if((http.readyState == 4) && (http.status == 200)){
    var response = http.responseText,
        elementid = 'ownedimage' + jsid,
        element = document.getElementById(elementid),
        image, alt, mouseoverstr, mouseoutstr;
    if (response == "1") {
        element.src = "images/collection/got.gif";
        element.alt = "you own this game";
        mouseoverstr = 'images/collection/del.gif';
        mouseoutstr = 'images/collection/got.gif';
    } else {
        element.src = "images/collection/add.gif";
        element.alt = "add this game to your collection";
        mouseoverstr = 'images/collection/add.gif';
        mouseoutstr = 'images/collection/add.gif';
    }
    //Create a holder if this is the first time for this elementid
    if(!handlers[elementid]) handlers[elementid] = {};
    //Remove old handlers
    element.removeEventListener('mouseover', handers[elementid].mouseover, false);
    element.removeEventListener('mouseout', handers[elementid].mouseout, false);
    //Store new handlers
    handlers[elementid].mouseover = function(){changeImageSrc('ownedimage' + jsid, mouseoverstr)};
    handlers[elementid].mouseout = function(){changeImageSrc('ownedimage' + jsid, mouseoutstr)};
    //Add hew handlers as listeners
    element.addEventListener('mouseover', handers[elementid].mouseover, false);
    element.addEventListener('mouseout', handers[elementid].mouseout, false);
  }
}
2 голосов
/ 02 января 2011

Пара вещей там:: -)

1) addEventListener и removeEventListener работают с более новой цепочкой обработчиков DOM2, которая полностью отделена от более старого стиля "DOM0" (атрибуты onXYZ). Таким образом, вы не можете удалить обработчик с помощью removeEventListener, который изначально был назначен с помощью onXYZ attriubte. Для этого присвойте "" свойству отраженного атрибута.

element.onmouseover = "";

2) Когда вы используете removeEventListener, вы должны передать такую ​​же ссылку на функцию , которую вы использовали изначально. Так что это никогда ничего не удалит:

element.removeEventListener('mouseout', function(){changeImageSrc('ownedimage' + jsid, 'images/collection/got.gif')}, false);

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

Я бы, вероятно, подошел к проблеме, имея отдельный обработчик событий, но затем изменив данные, с которыми он работает. Вы можете сделать это, сохранив URL альтернативного изображения на самом фактическом элементе, используя атрибут data-xyz (скажем, data-oversrc и data-stdsrc или что-то подобное). Затем две ваши функции (одна для наведения мыши, другая для наведения мыши), которые изменяют URL в зависимости от изображения:

function handleMouseOver() {
    this.src = this.getAttribute('data-oversrc');
}

function handleMouseOut() {
    this.src = this.getAttribute('data-stdsrc');
}

Я бы полностью отбросил обработчики onXYZ из элементов и заменил их одноразовыми addEventListener (attachEvent в IE), которые назначают оба из них.

data-xyz атрибуты, как и все пользовательские атрибуты, недопустимы в HTML4 и более ранних версиях. Начиная с HTML5, они проверяются, и, поскольку ни у одного из основных браузеров нет проблем с недопустимыми атрибутами, вы можете сразу начать использовать их.


Не по теме # 1 : В наши дни я обычно рекомендую использовать библиотеку JavaScript, чтобы сгладить различия в браузерах и реализовать для вас полезные функции. Например, ваш код, использующий addEventListener и removeEventListener, не будет работать в IE до IE9, потому что у него просто нет этих методов. Если вы используете библиотеку, такую ​​как jQuery , Прототип , YUI , Закрытие или любой из нескольких других , они будут заниматься этим для вас, чтобы вы могли сосредоточиться на собственной добавленной стоимости.

Не по теме # 2 : Событие mouseover происходит многократно, когда мышь проходит над элементом, и она и mouseout всплывают в DOM. Это означает, что для таких эффектов, как ваш, если вы не можете использовать CSS (и не можете использовать IE6), вам лучше использовать события mouseenter и mouseleave в большинстве случаев. Но это специфичные для IE события, которые не поддерживаются большинством других браузеров. Войдите в любую приличную библиотеку JavaScript. :-) Они будут эмулировать mouseenter и mouseleave в браузерах, которые не поддерживают их напрямую. Из списка выше я знаю наверняка, что jQuery и Prototype делают это; Я был бы удивлен, если бы другие не имели подобную функциональность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...