jQuery .change()事件仅触发一次。

13
我有一个应用程序,当DOM准备好时,从数据库检索项目名称。每个项目都添加到html <form>中的<select><option>中。一旦列表被填充,用户可以选择项目标题,这将请求与该项目相关的其余信息。
为了实现这一点,我使用了$.change() jQuery方法。不幸的是,当<select>元素被创建并添加到DOM中时,事件只会触发一次。从列表中选择另一个项目不会触发事件,因此也不会触发$.post()调用。
$(function(){
    getProjects();
    var firstLoad = true;

    $("select").change(retrieveProject); // Removed parenthesis based on answers

    // On page load, get project names from the database and add them to a Select form element
    function getProjects() {
        var selectionList;
        $.getJSON("php/getProjects.php", function (data) {
            selectionList = "<form><select>";
            for (var i = 0; i < data.length; i++) {
                selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
            }
            selectionList += "</select></form>";
        }).complete(function() {
           $('#project-selection-menu').append(selectionList).removeClass('hidden');
           firstLoad = false;
        });
    }

    function retrieveProject() {
        if ( firstLoad == true ){
            alert(firstLoad); // This alert fires
            return false;
        } else {
            alert(firstLoad); // This alert doesn't fire
            $.post("php/getProjects.php", function (data) { // This should have been "php/retrieveProject.php"!
                // Do stuff with the returned data
            }).complete(function() {
               console.log("Success.");
            });
        }
    }
)};

我省略了匿名函数和 getProjects(); 调用以减少显示的代码量,但这显然引起了更多混淆。 - Roy
5个回答

14

你没有正确设置事件处理程序:

$("select").change(retrieveProject);

在你的代码中,你在调用"retrieveProject"函数,而该函数调用的返回值作为"change"处理程序被传递(并且当然没有效果)。这就是为什么在页面加载时会出现事件生成的情况。

当你使用一个函数作为时,你不需要在函数引用后面加上()——你想要的是引用本身(在这种情况下是函数名),这就是需要传递给jQuery的东西。

还有一点很重要——确保你的代码在“ready”或“load”处理程序中运行,或者在页面上的<select>元素之后,如果脚本在文档头部,则它将在解析DOM之前运行,并且没有效果。(另一种处理方式是使用另一个答案中建议的.on()委托形式。)


此外:当你在"getProjects"中获取内容时,似乎覆盖了你的<select>元素。因此,你应该绝对使用委托形式:

$(document).on("change", "select", retrieveProject);

另外,你应该在“getProjects”中使用局部变量:

function getProjects() {
    var selectionList; // keep this local to the function - implicit globals are risky

    $.getJSON("php/getProjects.php", function (data) {
        selectionList = "<form><select>";
        for (var i = 0; i < data.length; i++) {
            selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
        }
        selectionList += "</select></form>";
    }).complete(function() {
       $('#project-selection-menu').append(selectionList).removeClass('hidden');
       firstLoad = false;
    });
}

@Roy,<select>元素上的“change”事件肯定是有效的。您在浏览器控制台中看到任何错误吗? - Pointy
@Roy,好的,我想我看到了问题 - 我会在答案中添加。 - Pointy
成功了,但你能否在你的回答中扩展“更多”部分?我是一名地理学家而不是程序员,这部分还有点模糊。 - Roy
2
@Roy 当你直接将事件处理程序绑定到元素上(这就是原始代码所做的),那么如果该元素消失,处理程序绑定也会丢失。然而,当你使用三个参数的 .on() 形式时,处理程序绑定“存在”于文档的根部。事件会在页面上元素的祖先链中“冒泡”,因此对 <select> 的更改将被该处理程序看到。这是一个非常有用的功能,这意味着无论发生什么事情,你的处理程序仍将起作用。 - Pointy
这是一个旧的帖子,但在我绞尽脑汁了三天后,这篇内容对我很有帮助。+1 谢谢! - Mike
显示剩余2条评论

7
你需要处理事件委托。
$(document).on('change', 'select', retrieveProject);

同时,请移除与方法retrieveProject相邻的()

3
您也可以使用以下方法来实现,这将有效地解决问题。
$("select").change(function(){retrieveProject()});

或者

$("select").on('change',function(){retrieveProject()});

1

这是您正在寻找的内容吗?为了在页面加载后运行getProjects,只需在$(document).ready()函数中调用它。此外,您需要正确设置change处理程序。请参考fiddle。

var firstLoad = true;
getProjects();
$("#selectTest").change(function(){
    retrieveProject();
});

// On page load, get project names from the database and add them to a Select form element
function getProjects() {
    $.getJSON("php/getProjects.php", function (data) {
        selectionList = "<form><select>";
        for (var i = 0; i < data.length; i++) {
            selectionList += "<option name='prjTitle'>" + data[i].ProjectTitle + "</option>";
        }
        selectionList += "</select></form>";
    }).complete(function() {
       $('#project-selection-menu').append(selectionList).removeClass('hidden');
       firstLoad = false;
    });
}

function retrieveProject() {
    if ( firstLoad == true ){
        alert(firstLoad); // This alert fires
        return false;
    } else {
        alert(firstLoad); // This alert doesn't fire
        $.post("php/getProjects.php", function (data) {
            // Do stuff with the returned data
        }).complete(function() {
           console.log("Success.");
        });
    }
}

http://jsfiddle.net/trevordowdle/Mf38E/


在原始帖子中留下了一些代码以保持简洁,但这导致了额外的混淆。使用.change()回调函数会导致两个警报都不触发。 - Roy
@Roy,你的选择元素是动态生成的吗?我给选择器添加了一个id。如果你的选择器不是动态创建的,那么你可以将其更改为$("select").change(function(){ retrieveProject(); });,否则就按照Krishna设置的方式进行。 - Trevor

0

试试这个:

$(document).bind('change', 'select', function(){
    retrieveProject();
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接