button.Activated function firing multiple times

So I have a local script that has a function, and inside of that function there is a for, i loop which is:

for i, tower in pairs(towers) do

Inside of that, I have a function for when a gui button is activated:

newButton.Activated:Connect(function()

Inside of that function, I have some more lines a ways down that says:

buyButton.Activated:Connect(function()
				interactItem(tower.Name)
				print("Interacted")
			end)

So, the newButton.Activated function opens a gui with a buyButton in it, which is where the buyButton is located. However, whenever I go to test, and I click the buyButton, this shows in my output:

As you can see, the function keeps calling multiple times and each time it’s called it increases by 1. I’m not exactly sure how to fix this or what is causing the issue. I’m guessing it’s probably because of the for i, loop, but I want to make sure before I mess around and ruin my script.

In case this has anything to do with it, in a server script I have the interactItem function being called (which is inside of the buyButton.Activated function, so the interactItem function calls when the buyButton is activated). Here is the interactItem function being called:

ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)

there is a for, i loop … Inside of that, I have a function

it sounds like your events are inside of the for loop which would be the reason it’ll fire multiple times

could you paste the code normally instead of breaking it up so we can see it properly

function updateItems()
	limit.Text = #playerData.SelectedTowers .. "/5"
	
	for i, tower in pairs(towers) do
		
		local df = gui.Container:FindFirstChild("DisplayFrame")
		local buyButton = df:FindFirstChild("BuyButton")
		local buyPrompt = df:FindFirstChild("BuyPrompt")
		local sf = df:FindFirstChild("StatsFrame")
		local oldButton = itemsFrame:FindFirstChild(tower.Name)
		if oldButton then
			oldButton:Destroy()
		end
		
		local newButton = itemsFrame.TemplateButton:Clone()
		newButton.Name = tower.Name
		newButton.TowerName.Text = tower.Name
		newButton.Image = tower.ImageAsset
		newButton.Visible = true
		newButton.LayoutOrder = tower.Price
		newButton.Parent = itemsFrame
		
		local status = getItemStatus(tower.Name)
		if status == "For Sale" then
			newButton.Status.Text = "$" .. comma(tower.Price)
		elseif status == "Equipped" then
			newButton.Status.Text = "✅ Equipped"
			newButton.Status.TextColor3 = Color3.new(0,0,0)
			newButton.BackgroundColor3 = Color3.new(0,0,0)
		else
			newButton.Status.Text = ""
		end
		
		newButton.Activated:Connect(function()
			local towerObject = towerFolder:FindFirstChild(tower.Name):Clone()
			local viewportFrame = gui.Container.DisplayFrame.ViewportFrame
			local worldModel = viewportFrame.WorldModel
			
			local dfStatus = getItemStatus(tower.Name)
			if dfStatus == "For Sale" then
				buyButton.Text = "$" .. comma(tower.Price)
				buyButton.TextColor3 = Color3.new(0, 0.666667, 0)
				buyPrompt.Visible = true
			elseif dfStatus == "Equipped" then
				buyButton.Text = "Owned"
				buyButton.TextColor3 = Color3.new(0.784314, 0.784314, 0.784314)
				buyButton.BackgroundColor3 = Color3.new(0,0,0)
				buyPrompt.Visible = false
			else
				buyButton.Text = "✅ Equipped"
				buyButton.TextColor3 = Color3.new(0.666667, 1, 1)
				buyButton.BackgroundColor3 = Color3.new(0,0,0)
				buyPrompt.Visible = false
			end
			
			df:FindFirstChild("Name").Text = tower.Name
			df:FindFirstChild("GoldPrice").Text = "$" .. towerFolder:FindFirstChild(tower.Name).Config.Price.Value
			sf.CooldownFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Cooldown.Value
			sf.DamageFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Damage.Value
			sf.RangeFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Range.Value
			
			worldModel:ClearAllChildren()
			towerObject.Parent = worldModel

			local Camera = Instance.new("Camera")
			Camera.Parent = viewportFrame
			viewportFrame.CurrentCamera = Camera

			Camera.CFrame = CFrame.new(towerObject.PrimaryPart.Position + towerObject.PrimaryPart.CFrame.LookVector * 3.5, towerObject.PrimaryPart.Position)

			Camera.CFrame = towerObject.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(-195), 0) * CFrame.new(0,0,3.5)
			
			local outline = ReplicatedStorage.Misc:FindFirstChild("TowerOutline"):Clone()
			outline.Parent = towerObject
			
			playAnimation(towerObject, "Idle")
			
			buyButton.Activated:Connect(function()
				interactItem(tower.Name)
				print("Interacted")
			end)
			
		end)
	end
end

That’s the whole function, by the way I know that’s the reason it fires multiple times, but I can’t figure out an alternative to the code I have to fix it.

you need to take your events out of the for loop

function updateItems()
	limit.Text = #playerData.SelectedTowers .. "/5"

	for i, tower in pairs(towers) do

		local df = gui.Container:FindFirstChild("DisplayFrame")
		local buyButton = df:FindFirstChild("BuyButton")
		local buyPrompt = df:FindFirstChild("BuyPrompt")
		local sf = df:FindFirstChild("StatsFrame")
		local oldButton = itemsFrame:FindFirstChild(tower.Name)
		if oldButton then
			oldButton:Destroy()
		end

		local newButton = itemsFrame.TemplateButton:Clone()
		newButton.Name = tower.Name
		newButton.TowerName.Text = tower.Name
		newButton.Image = tower.ImageAsset
		newButton.Visible = true
		newButton.LayoutOrder = tower.Price
		newButton.Parent = itemsFrame

		local status = getItemStatus(tower.Name)
		if status == "For Sale" then
			newButton.Status.Text = "$" .. comma(tower.Price)
		elseif status == "Equipped" then
			newButton.Status.Text = "✅ Equipped"
			newButton.Status.TextColor3 = Color3.new(0,0,0)
			newButton.BackgroundColor3 = Color3.new(0,0,0)
		else
			newButton.Status.Text = ""
		end
	end
	
	newButton.Activated:Connect(function()
		local towerObject = towerFolder:FindFirstChild(tower.Name):Clone()
		local viewportFrame = gui.Container.DisplayFrame.ViewportFrame
		local worldModel = viewportFrame.WorldModel

		local dfStatus = getItemStatus(tower.Name)
		if dfStatus == "For Sale" then
			buyButton.Text = "$" .. comma(tower.Price)
			buyButton.TextColor3 = Color3.new(0, 0.666667, 0)
			buyPrompt.Visible = true
		elseif dfStatus == "Equipped" then
			buyButton.Text = "Owned"
			buyButton.TextColor3 = Color3.new(0.784314, 0.784314, 0.784314)
			buyButton.BackgroundColor3 = Color3.new(0,0,0)
			buyPrompt.Visible = false
		else
			buyButton.Text = "✅ Equipped"
			buyButton.TextColor3 = Color3.new(0.666667, 1, 1)
			buyButton.BackgroundColor3 = Color3.new(0,0,0)
			buyPrompt.Visible = false
		end

		df:FindFirstChild("Name").Text = tower.Name
		df:FindFirstChild("GoldPrice").Text = "$" .. towerFolder:FindFirstChild(tower.Name).Config.Price.Value
		sf.CooldownFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Cooldown.Value
		sf.DamageFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Damage.Value
		sf.RangeFrame:FindFirstChild("Title").Text = towerFolder:FindFirstChild(tower.Name).Config.Range.Value

		worldModel:ClearAllChildren()
		towerObject.Parent = worldModel

		local Camera = Instance.new("Camera")
		Camera.Parent = viewportFrame
		viewportFrame.CurrentCamera = Camera

		Camera.CFrame = CFrame.new(towerObject.PrimaryPart.Position + towerObject.PrimaryPart.CFrame.LookVector * 3.5, towerObject.PrimaryPart.Position)

		Camera.CFrame = towerObject.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(-195), 0) * CFrame.new(0,0,3.5)

		local outline = ReplicatedStorage.Misc:FindFirstChild("TowerOutline"):Clone()
		outline.Parent = towerObject

		playAnimation(towerObject, "Idle")

	end)
	
	buyButton.Activated:Connect(function()
		interactItem(tower.Name)
		print("Interacted")
	end)
end

So, what would I do to call the cloned newButton? Also without the for loop how am I supposed to call the tower etc.?

you’re right i realized you’re making new buttons

how many times are you calling `updateItems’ function

I call it twice. Once when the shop is opened, which is what all of this is in, and once when the interactItem function is called:

local function interactItem(itemName)
	local data = interactItemFunc:InvokeServer(itemName)
	if data	then
		playerData = data
		updateItems()
	end
end

I was looking at the GuiButton Documentation and i think you should use .MouseButton1Click
instead of .Activated, as it will only play once, but even if that doesn’t work then you should try replacing the :Connect() with :Once().