【问题标题】:JavaScript addEventListener & initializing function conflicting, trying to update global variableJavaScript addEventListener & 初始化函数冲突,试图更新全局变量
【发布时间】:2018-01-27 19:19:01
【问题描述】:

我正在开发一个游戏交互菜单,它是一个 HTML 菜单,它会根据玩家当前的工作来显示操作。

我的问题:我通过 window.addEventListener 检索有关球员工作的信息。它正在从我的游戏 Lua 文件发送到 JavaScript。我可以确认这是有效的。但是,在 JavaScript 部分存在问题。

作业正在 item.updateJob 下的 addEventListener 中存储/检索/更新,作为名为 playerJob 的变量。这也将更新具有相同名称的全局变量。正如您在代码中看到的,init() 函数在 window.addEventListener 之前运行。 init() 必须使用更新的全局变量 playerJob 来显示正确的菜单,但事实并非如此。在控制台中调试它时,它告诉我它是“未定义的”。我试图将 window.addEventListener 放在 §( function ... 代码中的 init() 函数上方,但这没有做任何事情。

如何确保 window.addEventListener 正在运行并在 init() 函数执行任何操作之前设置 playerJob 变量?我尝试在顶部放置一个全局变量并这样做:playerJob =“police”,然后它工作正常。有谁知道我该怎么做?

    var playerJob;

    $( function() {
        // Adds all of the correct button actions 
            console.log("Before int: ", playerJob)
            init();

        // Gets the interactionmenu div container 
        var interactionContainer = $( "#interactionmenu" );

        // Listens for NUI messages from Lua 
        window.addEventListener( 'message', function( event ) {
            var item = event.data;


            // Show the menu 
            if ( item.showmenu ) {
                ResetMenu()
                interactionContainer.show();
            }

            if ( item.hidemenu ) {
                interactionContainer.hide(); 
            }

            // This will update the players job, and does work!
            if ( item.updateJob ) {
                playerJob = item.job; 
                setJob(playerJob);
                console.log("playerJob set to: ", playerJob)
            }                   
        } );
    } )

    // Hides all div elements that contain a data-parent, in
    // other words, hide all buttons in submenus. 
    function ResetMenu() {
        $( "div" ).each( function( i, obj ) {
            var element = $( this );

            if ( element.attr( "data-parent" ) ) {
                element.hide();
            } else {
                element.show();
            }
        } );
    }

    // Configures every button click to use its data-action, or data-sub
    // to open a submenu. 
    function init() {
        // Loops through every button that has the class of "menuoption"
        $( ".menuoption" ).each( function( i, obj ) {

            // If the button has a data-action, then we set it up so when it is 
            // pressed, we send the data to the lua side. 
            if ( $( this ).attr( "data-action" ) ) {
                $( this ).click( function() { 
                    var data = $( this ).data( "action" ); 

                    sendData( "ButtonClick", data ); 
                } )
            }

            // If the button has a data-sub, then we set it up so when it is 
            // pressed, we show the submenu buttons, and hide all of the others.
            if ( $( this ).attr( "data-sub" ) ) {
                $( this ).click( function() {
                    var menu = $( this ).data( "sub" );
                    var element = $( "#" + menu ); 
                    element.show();
                    $( this ).parent().hide();  
                } )
            }

            // Here I'd like to know the playerJob, and want via item.updateJob in the addEventListener
            // However it returns undefined, no matter if I place the init() above or below
            // My question is: how can I make sure the item.updatePlayer job in the addEventListener updates
            // the variable globally and makes sure that it does that before this function is being run? 
            console.log("right before data-require: ", playerJob)   // returns undefined X amount of times.         

            if ( ( $( this ).attr ( "data-require" ) == "police" && playerJob != "police") ) {
                $( this ).hide();
            }

            else if ( ( $( this ).attr ( "data-require" ) == "mecano" && playerJob != "mecano") ) {
                $( this ).hide();
            }

            else if ( ( $( this ).attr ( "data-require" ) == "ambulance" && playerJob != "ambulance") ) {
                $( this ).hide();
            }
        } );

    }

    // Send data to lua for processing.
    function sendData( name, data ) {
        $.post( "http://wk_interactionmenu/" + name, JSON.stringify( data ), function( datab ) {
            if ( datab != "ok" ) {
                console.log( datab );
            }            
        } );
    }

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:

    如果我正确理解了这个问题,您希望 init 仅在收到 message 事件后运行,因此已设置 playerJob

    我认为您不理解的是 addEventListener 调用没有运行侦听器,它只是在设置它。问题是message 事件要到稍后才会发生。这就是为什么当您查看playerJob 时会得到undefined

    但您不希望init 运行多次,因此不希望它从事件处理程序中运行。

    您可以在同一位置设置标志var didInit = false;,而不是当前调用init()。然后,在您的事件处理程序中,设置playerJob 后,检查didInit 的状态。如果还不是真的,请将其设置为 true 并运行您的 init 函数。

    这是你的 onLoad ($(function(){...) 函数的替代品,它应该可以完成这项工作:

    $( function() {
        // Did we initialize yet?
        var didInit = false;
    
        // Gets the interactionmenu div container
        var interactionContainer = $( "#interactionmenu" );
    
        // Listens for NUI messages from Lua 
        window.addEventListener( 'message', function( event ) {
            var item = event.data;
    
    
            // Show the menu
            if ( item.showmenu ) {
                ResetMenu()
                interactionContainer.show();
            }
    
            if ( item.hidemenu ) {
                interactionContainer.hide();
            }
    
            // This will update the players job, and does work!
            if ( item.updateJob ) {
                playerJob = item.job;
                setJob(playerJob);
                console.log("playerJob set to: ", playerJob)
    
                // Initialize if we didn't yet
                if (!didInit) {
                    didInit = true;
                    init();
                }
            }
        } );
    } )
    

    玩家的工作会改变吗?如果是这样,您可能想要再次运行隐藏按钮的初始化代码部分,并且您想要更改它以显示以前隐藏的任何现在相关的按钮。

    如果是这种情况,可能值得将 init 分成两部分 - 一个可以在开始时运行(就像您的原始代码一样),它不依赖于 playerJob,另一个不依赖于 playerJob可以在任何时候运行playerJob 更改(包括第一次设置)并更新 UI。然后,您可以将其作为事件处理程序的一部分运行(不带任何条件或 didInit 标志)。

    【讨论】:

    • init() 函数运行为菜单准备一些按钮,它必须首先运行以隐藏一些玩家无法访问的按钮,除非玩家获得正确的工作。这就是为什么我发现在运行 init() 函数的同时检测玩家的工作很复杂。一旦我在游戏中按下热键按钮,它就会运行 init 函数来准备按钮 - 然后显示它。 data-require 隐藏了玩家无权访问的所有按钮,但如果玩家获得了例如“警察”工作,他应该可以访问 data-require = “police”。
    • 我刚刚编辑了我的答案,并提供了一些关于 playerJob 是否会发生变化的更多建议——其中一些与您在此处的评论相关。问题是,在事件发生之前您无法知道工作,那么您怎么知道要隐藏哪些按钮?因此,如果您在知道之前还不想显示任何现有的 UI,那么您需要一个新的 UI。也许是灰色/禁用的按钮,或者加载屏幕?
    • 这是一个“子脚本”,一旦播放器加载,它就会立即将消息发送到 addEventListener。尝试了代码,没有用 - 只显示了所有按钮,包括那些不应该显示的按钮。
    • 您仍然不明白事件不会立即发生。您说它“立即”出现,但它与您的代码是异步的。你说它不起作用,但我不明白为什么它不起作用。在init 函数的开头添加一条日志消息。然后您应该看到playerJob set to: police,然后是init running。你看到了什么?
    • 我又试了一次,现在可以了。非常感谢,一直在为此苦苦挣扎。对旗帜和事件不熟悉,谢谢!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 2016-10-03
    • 1970-01-01
    相关资源
    最近更新 更多