简体   繁体   English

如何使用JavaScript动态更改事件处理程序?

[英]How to change event handlers dynamically with JavaScript?

I'm pretty new to anything AJAX and ran into the following problem. 我对任何AJAX都是新手,并遇到了以下问题。 I hope someone here is able to help me out. 我希望这里有人能够帮助我。 I'm sure it's an easy fix but I just don't see it :( 我敢肯定这是一个简单的解决方法,但我只是没有看到它:(

What I'm trying to do is change the onmouseover and onmouseout events dynamically for the element, here are the relevant bits of code: 我想做的是为该元素动态更改onmouseover和onmouseout事件,这是相关的代码位:

The HTML Element (notice there are multiple of these, hence the dynamic part of their id) HTML元素(请注意,其中有多个元素,因此是其ID的动态部分)

<?
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>
<?} ?>

The JavaScript function: JavaScript函数:

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

The (relevant) AJAX code: (相关的)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);
}
}

It seems to work fine at first but produces some weird behaviour. 起初它似乎工作正常,但会产生一些奇怪的行为。 The referenced PHP works fine and produces the correct response. 引用的PHP可以正常工作并产生正确的响应。 The srcand alt of the image get changed as well. 图像的高度也会更改。 In fact, it first looks like the new mouseover/out work too. 实际上,它首先也看起来像是新的鼠标悬停/悬停工作。 But when you click on more than one image on the site (which have different IDs) they suddenly start influencing each other. 但是,当您单击站点上的多个图像(具有不同的ID)时,它们突然开始相互影响。 When you mouseover over one, the other changes its image aswell. 当您将鼠标悬停在一个上时,另一个也会更改其图像。 Why is this happening? 为什么会这样呢? I really am clueless as the jsid part is fine and I don't understand why the mouseover suddenly changes two images. 我真的很笨,因为jsid部分还不错,而且我不明白为什么鼠标悬停会突然更改两个图像。 It looks as if multiple eventhandlers for different IDs are assigned to the same image element. 似乎将多个具有不同ID的事件处理程序分配给同一图像元素。 No idea why that is though. 不知道为什么会这样。 I hope some of you with more AJAX knowledge can help me here, quite frustrated :( 我希望一些了解AJAX的人能在这里对我有所帮助,

A couple of things there: :-) 那里有几件事::-)

1) addEventListener and removeEventListener work with the newer DOM2 handler chain, which is completely separate from the older "DOM0" style (the onXYZ attributes). 1) addEventListenerremoveEventListener与较新的DOM2处理程序链一起使用,这与较旧的“ DOM0”样式( onXYZ属性)完全分开。 So you can't remove a handler via removeEventListener that you've originally assigned via an onXYZ attriubte. 因此,您无法通过最初由onXYZ属性分配的removeEventListener删除处理程序。 To do that, assign "" to the attribute's reflected property. 为此,请将“”分配给属性的反射属性。

element.onmouseover = "";

2) When you use removeEventListener , you must pass in the same function reference that you used originally. 2)使用removeEventListener ,必须传递最初使用的相同的函数引用。 So this will never remove anything: 因此,这将永远不会删除任何内容:

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

...because it creates a brand new function to pass into removeEventListener , and since that function isn't on the event handler chain, the call is ignored. ...由于它创建了一个全新的函数传递给removeEventListener ,并且由于该函数不在事件处理程序链中,因此该调用将被忽略。

I'd probably approach the problem by having a single event handler, but then changing the data it works with. 我可能会通过使用单个事件处理程序来解决该问题,但随后更改其处理的数据。 You can do that by storing the alternate image URLs on the actual element itself, using a data-xyz attribute (say, data-oversrc and data-stdsrc or some such). 您可以通过使用data-xyz属性(例如, data-oversrcdata-stdsrc等)将备用图像URL存储在实际元素本身data-stdsrc Then your two functions (one for mouseover, one for mouseout) that change the URL based on the image: 然后,您有两个函数(一个用于鼠标悬停,一个用于鼠标移出)可根据图像更改URL:

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

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

I'd drop the onXYZ handlers from the elements entirely, and replace them with a one-time addEventListener ( attachEvent on IE) that assigns both of those. 我将完全从元素中删除onXYZ处理程序,并用一次性的addEventListener (在IE上为attachEvent )替换它们,以分配这两个处理程序。

data-xyz attributes, like all custom attributes, are invalid in HTML4 and earlier. 像所有自定义属性一样, data-xyz属性在HTML4及更早版本中无效。 As of HTML5, they validate, and since no major browser has a problem with invalid attributes anyway, you can start using them right away. 从HTML5开始,它们可以验证,并且由于没有主流浏览器仍然存在无效属性的问题,因此您可以立即开始使用它们。


Off-topic #1 : These days, I usually recommend using a JavaScript library to smooth over browser differences and implement useful functionality for you. 离题1 :这些天,我通常建议使用JavaScript库来缓解浏览器差异并为您实现有用的功能。 For instance, your code using addEventListener and removeEventListener will fail on IE prior to IE9 because it simply doesn't have those methods. 例如,使用addEventListenerremoveEventListener代码将在IE9之前的IE上失败,因为它根本没有这些方法。 If you use a library like jQuery , Prototype , YUI , Closure , or any of several others , they'll deal with that stuff for you so you can focus on your own value-add. 如果您使用jQueryPrototypeYUIClosure其他几种库 ,它们将为您处理这些东西,因此您可以专注于自己的增值。

Off-topic #2 : The mouseover event happens repeatedly when the mouse is passing over the element, and both it and mouseout bubbles up the DOM. 离题2 :当鼠标经过元素上方时, mouseover事件重复发生,并且元素和mouseout都使DOM冒泡。 This means that for hover effects like yours, if you can't use CSS (and you can't on IE6), you're better off using the mouseenter and mouseleave events, most of the time. 这意味着对于像您这样的悬停效果,如果您不能使用CSS(并且不能在IE6上使用),则大多数情况下最好使用mouseentermouseleave事件。 But those are IE-specific events not supported by most other browsers. 但是这些是大多数其他浏览器都不支持的IE特定事件。 Enter any decent JavaScript library. 输入任何不错的JavaScript库。 :-) They'll emulate mouseenter and mouseleave on browsers that don't support them directly. :-)它们将在不直接支持它们的浏览器上模拟mouseentermouseleave Of the list above, I know for certain that jQuery and Prototype do that; 在上面的列表中,我肯定知道jQuery和Prototype可以做到这一点; I'd be surprised if the others don't have similar functionality. 如果其他功能没有类似功能,我会感到惊讶。

The issue is the removeEventListener , it's not working like you think it is. 问题是removeEventListener ,它无法像您认为的那样工作。 What's happening is when you do this: 发生的事情是当您执行此操作时:

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

That function() { } is a new anonymous function you just created, not the existing one on the element as a listener...so when it goes to remove that listener, it just isn't there, because that was a different anonymous function (even if it had the exact same code, it's a different reference) that you assigned via addEventListener . function() { }是您刚刚创建的一个新的匿名函数,而不是元素上作为侦听器的现有函数……因此,当删除侦听器时,它就不存在了,因为那是另一个匿名函数您通过addEventListener分配的函数(即使它具有完全相同的代码,这是一个不同的引用)。

There are a few work-arounds for this, given that you're making AJAX requests I'm assuming you're not making hundreds/thousands here, so you could just store the handlers you assign for removing them later, like this: 鉴于您正在发出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);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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