Connections won't fire

I’m running into an odd issue when creating MouseEnter, MouseLeave and MouseButton1Click connections inside a ModuleScript:

  • I have ImageButtons in PlayerGui.MainUI.BottomBar.Container.Bar
  • Direct LocalScript connections work perfectly:
local button = player.PlayerGui.MainUI.BottomBar.Container.Bar.Configuration
button.MouseButton1Click:Connect(function() print("click") end)
button.MouseEnter:Connect(function() print("enter") end)
button.MouseLeave:Connect(function() print("leave") end)

But when I connect the same events in a module function inside PlayerGui, like this:

function handle_button(button, module)
    print("Handling button", button)

    button.MouseButton1Click:Connect(function()
        module:open()
    end)

    button.MouseEnter:Connect(function()
        print("hover")
    end)

    button.MouseLeave:Connect(function()
        print("unhover")
    end)

    print("Handled")
end

handle_button(buttons.Configuration, {open = function() print("opened") end})

the events never fire, even though debug prints inside handle_button print normally. The worst part is that the connections ARE created and connected, just not fired

What I already checked:

  • Buttons exist and are fully parented when the module function runs
  • Active and interacteable
  • No overlay frames or CanvasGroups blocking input
2 Likes

it's global function with connections inside of function... (just make connection to be outside function)

or try return

1 Like

is ‘handling button’ and ‘handled’ being printed?

When you use direct indexing for a screen gui found on the PlayerGui, it will most likely not exist yet, local or server. I know this from experience

Try replacing this line:
local button = player.PlayerGui.MainUI.BottomBar.Container.Bar.Configuration
With this line;
local button = player.PlayerGui:WaitForChild("MainUI").BottomBar.Container.Bar.Configuration

Tested working in the Studio..

--ModuleScript in ReplicatedStorage.MyModule
local mod = {}

function mod.handle_button(button, actions)
	if actions.click then
		button.MouseButton1Click:Connect(function()
			actions.click(button)
		end)
	end
	if actions.enter then
		button.MouseEnter:Connect(function()
			actions.enter(button)
		end)
	end
	if actions.leave then
		button.MouseLeave:Connect(function()
			actions.leave(button)
		end)
	end
end

function mod.handle_all_buttons(container, actions)
	for _, button in ipairs(container:GetChildren()) do
		if button:IsA("ImageButton") then
			mod.handle_button(button, actions)
		end
	end
end

return mod
--LocalScript in StarterPlayerScripts
task.wait(3) --just a stall for this test, GUI setup time

local player = game.Players.LocalPlayer
local mod = require(game.ReplicatedStorage:WaitForChild("MyModule"))

local gui = player:WaitForChild("PlayerGui"):WaitForChild("MainUI")
local bar = gui:WaitForChild("BottomBar"):WaitForChild("Container"):WaitForChild("Bar")

mod.handle_all_buttons(bar, {
	click = function(button)
		print("Clicked:", button.Name)
	end,
	enter = function(button)
		print("Hovered:", button.Name)
	end,
	leave = function(button)
		print("Unhovered:", button.Name)
	end
})
A quick testing GUI
--LocalScript in StarterPlayerScripts
local player = game.Players.LocalPlayer
local gui = Instance.new("ScreenGui")
gui.Name = "MainUI"
gui.ResetOnSpawn = false
gui.Parent = player:WaitForChild("PlayerGui")

local bottomBar = Instance.new("Frame")
bottomBar.Name = "BottomBar"
bottomBar.Size = UDim2.new(1, 0, 0, 100)
bottomBar.Position = UDim2.new(0, 0, 1, -100)
bottomBar.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
bottomBar.Parent = gui

local container = Instance.new("Frame")
container.Name = "Container"
container.Size = UDim2.new(1, 0, 1, 0)
container.BackgroundTransparency = 1
container.Parent = bottomBar

local bar = Instance.new("Frame")
bar.Name = "Bar"
bar.Size = UDim2.new(1, 0, 1, 0)
bar.BackgroundTransparency = 1
bar.Parent = container

for i = 1, 3 do
	local btn = Instance.new("ImageButton")
	btn.Name = "Button" .. i
	btn.Size = UDim2.new(0, 100, 0, 60)
	btn.Position = UDim2.new(0, 20 + (i - 1) * 120, 0.5, -30)
	btn.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
	btn.AutoButtonColor = true
	btn.Parent = bar
end

This is a subtle Roblox quirk that comes up when connecting UI events from ModuleScripts vs LocalScripts. The key issue is which context the code is running in.

ModGuiTest_HoverClickLeave.rbxl (58.7 KB)

1 Like

Yes, both are getting printed, and .Connected prints as true

Both UIs already exist, and when you print everything, it matches perfectly

I appreciate the example, but that approach probably won’t change anything. The module is inside PlayerGui and is already being required from a local script (it waits a few seconds before doing so) and then calls _init() , which uses the handle_button function.

function module:_init()
	if _init then
		return
	end
	
	print(`Initiating module {script.Name} ...`)
	_init = true
	
	handle_button(buttons.Configuration, configuration)
	handle_button(buttons.Thermodynamics, thermodynamics)
	handle_button(buttons.Radar, radar)
	handle_button(buttons.RobuxShop, robuxshop)
	
	print(`Initiation finalized`)
end

Also, my project is structured around modules handling their own parts of the UI, so I’m trying to keep the connection logic inside those modules. Moving it into a LocalScript wouldn’t really fit that structure, and I don’t think it would fix the issue either.

So even if I followed that setup, it wouldn’t really fix the root issue. The events don’t fire when the connections are made inside the module, but they work fine when connected directly from a local script.

Just to clarify, all of this runs client-side and the issue seems unrelated to context. Everything prints fine, the GUI objects exist, and the connections are made successfully; they just don’t fire.

I can provide more detail if it is necessary.

1 Like

Did a compiled run and this was working
Going to PMs with this…

Do you at any point delete the script that invokes the ModuleScript?

1 Like

I actually do. I have a local script that initializes the modules and then gets deleted after 5 seconds.

Yeah, that’s probably the problem. Code running from ModuleScripts inherit the context(?) of the calling script, which means that any functions/threads created in them are treated as dead when the script is destroyed.

Should I not be deleting the script?

Oh alright, i will do that now

That actually fixed it! Thank you so much!
Now it annoys me knowing that I spent 7 hours trying to figure out the issue just for it to be only this.

I believe a workaround, if you want to keep the same style of your code, would be to fire a BindableEvent that the ModuleScript listens to that creates the desired functions. You can fire the event through the module’s functions, too. It just needs to have the function ran from the ModuleScript’s perspective instead of the script.

1 Like

I was wondering how you were ever going to get out of this. Broke that down to the only possible issue last night, other than deleting a script. I don’t think I would have ever thought of that. :smirking_face:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.