Creating HYPERLINKS (for internal documentation)

I’m developing an INTERNAL Online Help for my game, ie, a manual inside Frames (because Roblox blocks access to EXTERNAL hyperlinks).

So I have a frame with a TextLabel like this:

image

As in HTML Links Hyperlinks, I would like that when the player hovers over the link it changes color and when he clicks on the link it opens another frame.

But I came across this limitation of Roblox, which doesn’t allow to the creation of hyperlinks.

I also noticed that the seamless concatenation between objects of type TextLabel and TextButton is a very annoying thing to do manually.


So I decided to come up with a solution for this, where, providing a single TextLabel like this…

… and creating something like this…

To get to this result, you must provide an original TextLabel containing your normal text, but wrapping the part of the text you want to hyperlink with the same HTML syntax.
Example:

<a href="link1">Click here</a> or also <a href="link2">click on this link</a> to open some links.

So here’s the code you can put on your LocalScript:

-- from: rogeriodec_games

local function Hyperlinks(TextLabel, LinkColor, HoverColor)

	local function ExtractTextAndLinks(String)
		local Sequence = {}
		repeat
			local PosOpenA = string.find(String, '<a', 1)
			if PosOpenA then
				if PosOpenA > 1 then 
					local Text = string.sub(String, 1, PosOpenA-1)
					table.insert(Sequence, {Type = 'Text', Text = Text})
				end
				local PosCloseA = string.find(String, '</a>', PosOpenA+2)
				if not PosCloseA then
					warn('No closing </a>')
					return
				end
				local Tag = string.sub(String, PosOpenA, PosCloseA+3)
				local PosHref = string.find(Tag, 'href', 4)
				if not PosHref then
					warn('No href')
					return
				end
				local Key = Tag:split('"')
				if #Key ~= 3 then
					warn('Incomplete quotes on href')
					return
				end
				local Href = Key[2]
				local PosCloseA2 = string.find(Key[3], '</a>', 1)
				local Text = string.sub(Key[3], 2, PosCloseA2-1)
				table.insert(Sequence, {Type = 'Link', Href = Href, Text = Text})
				String = string.sub(String, PosCloseA+4)			
			end
		until not PosOpenA
		if String then
			table.insert(Sequence, {Type = 'Text', Text = String})
		end
		return Sequence
	end
	
	local function Trim(str)
		return string.gsub(str, "^%s*(.-)%s*$", "%1")
	end
	
	local Sequence = ExtractTextAndLinks(TextLabel.Text)
	if #Sequence == 1 and Sequence[1].Type == 'Text' then return end

	local Pos = Vector2.new(0, 0)
	
	for Id, Object in ipairs(Sequence) do
		local Type = Object.Type == 'Link' and 'TextButton' or 'TextLabel'
		local Text = Instance.new(Type)
		Text.Name = string.format("%02i", Id) .. '_' .. Type 
		Text.Parent = TextLabel.Parent
		Text.AutomaticSize = TextLabel.AutomaticSize
		Text.BackgroundColor3 = TextLabel.BackgroundColor3
		Text.BorderColor3 = TextLabel.BorderColor3
		Text.BorderMode = TextLabel.BorderMode
		Text.BorderSizePixel = TextLabel.BorderSizePixel
		Text.LayoutOrder = TextLabel.LayoutOrder
		Text.SizeConstraint = TextLabel.SizeConstraint
		Text.Visible = TextLabel.Visible
		Text.ZIndex = TextLabel.ZIndex
		Text.AutoLocalize = TextLabel.AutoLocalize
		Text.BackgroundTransparency = TextLabel.BackgroundTransparency
		Text.FontFace = TextLabel.FontFace
		Text.RichText = true
		Text.Text = Object.Text
		Text.TextColor3 = TextLabel.TextColor3
		Text.TextSize = TextLabel.TextSize
		Text.TextStrokeColor3 = TextLabel.TextStrokeColor3
		Text.TextStrokeTransparency = TextLabel.TextStrokeTransparency
		Text.TextTransparency = TextLabel.TextTransparency
		Text.TextTruncate = TextLabel.TextTruncate
		Text.TextXAlignment = TextLabel.TextXAlignment
		Text.TextYAlignment = TextLabel.TextYAlignment
		Text.Position = UDim2.new(0, Pos.X, 0, Pos.Y)
		
		local Size
		local Text2 = ''
		repeat		
			Size = Text.TextBounds
			Text.Size = UDim2.new(0, Size.X, 0, Size.Y)
			if Pos.X + Size.X > TextLabel.Parent.AbsoluteSize.X then -- whether text must be wrapped
				if Object.Type == 'Link' then
					Pos = Vector2.new(0, Pos.Y + Size.Y)
					--Size = Vector2.new(0, 0)
					Text.Position = UDim2.new(0, 0, 0, Pos.Y)
				else
					repeat
						local Char = string.sub(Text.Text, -1)
						Text.Text = string.sub(Text.Text, 1, string.len(Text.Text) - 1)
						Text2 = Char .. Text2
					until not Char or Char == ' '
				end
			elseif Text2 ~= '' then
				Text2 = Trim(Text2)
				table.insert(Sequence, Id + 1, {Type = 'Text', Text = Text2})
				Pos = Vector2.new(0, Pos.Y + Size.Y)
				Size = Vector2.new(0, 0)
				Text2 = ''
			end
		until Text2 == ''
		Pos += Vector2.new(Size.X, 0)
		if Type == 'TextButton' then
			Text.Text = '<u>' .. Text.Text .. '</u>'
			if LinkColor then Text.TextColor3 = LinkColor end
			local TextColor3 = Text.TextColor3

			Text.MouseEnter:Connect(function()
				if HoverColor then Text.TextColor3 = HoverColor end
			end)

			Text.MouseLeave:Connect(function()
				Text.TextColor3 = TextColor3
			end)

			Text.MouseButton1Down:Connect(function()
				print(Object.Href)
			end)

		end
	end
	
	TextLabel:Destroy() -- remove the original TextLabel (with HTML Tags)
end

-- START ---

local TextLabel = game.Players.LocalPlayer.PlayerGui.ScreenGui.Frame2.TextLabel -- your original TextLabel (with the HTML tags)
Hyperlinks(TextLabel, Color3.fromRGB(255, 0, 0), Color3.fromRGB(127, 127, 127)) -- create many TextLabels and TextButtons as children of the same original TextLabel's Parent

Edit v1.2

Did some improvements to wrap the resulting text when its size is greater than its Parent’s size:

image

19 Likes

Wouldn’t just be easier to simply use RichText by default, and just add in the Input functions instead of creating a whole module that converts HTML, or am I understanding this wrong.

1 Like

:point_up_2:t2:


Hyperlinks lead to sites (or experiences in roblox), it does not seem like this does that.

Anyways, Great Tutorial.

Hyperlinks lead to ANYTHING, depending on where’s your content.
Here I’m using the same concept of the HTML to lead to a internal documentation (Frames).

1 Like

However doesn’t it seem tedious to use HTML in a place where it’s not meant to be used?

thanks for this amazing [Hyperlink Blocked] tutorial!!!

3 Likes

This is useless considering you can’t actually make it lead to anywhere.

2 Likes

@sasqwothlives @SpiralAPI
Sorry, it wasn’t clear at the beginning that it was to be used in an internal manual inside the game, with frames.
I changed the initial sentence in the OP.

If it did then there would be a lot of scammers sending users to off-site fake robux scam sites

1 Like

Works well however it doesn’t support line breaks/new line characters. The order get’s all jacked up.
image

Am I doing something wrong or is a space here unsupported?
image

RichText is also using a HTML-inspired syntax.

SmartSelect_20221016_083925_Chrome

This module basically creates a hyperlink effect and allows you to execute code on clicking.

2 Likes

did you find a fix for this?

im having the same problem

Meh. Doesn’t support parametres. Shame but nice work regardless.

1 Like

After long hour, I’ve rewritten the code to allow parametres. No idea what half I’ve written does, but it works so I won’t mess with it. Only kidding.

return function(TextLabel: TextLabel)
	local Arg
	local function ExtractTextAndLinks(String)
		local Sequence = {}
		repeat
			local PosOpenA = string.find(String, '<a', 1)
			if PosOpenA then
				if PosOpenA > 1 then
					local Text = string.sub(String, 1, PosOpenA-1)
					table.insert(Sequence, {Type = 'Text', Text = Text})
				end
				local PosCloseA = string.find(String, '</a>', PosOpenA+2)
				if not PosCloseA then
					warn('No closing </a>')
					return
				end
				local Tag = string.sub(String, PosOpenA, PosCloseA+3)
				local PosHref = string.find(Tag, 'href', 4)
				if not PosHref then
					warn('No href')
					return
				end
				local Key = Tag:split('"')
				print(Key)
				local PosArg = string.find(Tag, 'arg', 4)
				if #Key ~= 3 and not PosArg then
					warn('Incomplete quotes on href - arg missing')
					return
				elseif #Key ~= 5 and PosArg then
					warn('Incomplete quotes on href - arg found') --included
					return
				end
				local Href = Key[2]
				Arg = Key[4]
				local CloseTagIndex = PosArg and 5 or 3
				local PosCloseA2 = string.find(Key[CloseTagIndex], '</a>', 1)
				local Text = string.sub(Key[CloseTagIndex], 2, PosCloseA2-1)
				table.insert(Sequence, {Type = 'Link', Href = Href, Text = Text})
				String = string.sub(String, PosCloseA+4)
			end
		until not PosOpenA
		if String then
			table.insert(Sequence, {Type = 'Text', Text = String})
		end
		return Sequence
	end

	local function Trim(str)
		return string.gsub(str, "^%s*(.-)%s*$", "%1")
	end

	local Sequence = ExtractTextAndLinks(TextLabel.Text)
	if #Sequence == 1 and Sequence[1].Type == 'Text' then return end
	
	local Pos = Vector2.new(0, 0)--*1.7160493827
	
	local Collection = Instance.new("Frame", TextLabel.Parent)
	Collection.Name = TextLabel.Name
	Collection.Size = TextLabel.Size
	Collection.BackgroundTransparency = 1
	Collection.Position = TextLabel.Position

	for Id, Object in ipairs(Sequence) do
		local Type = Object.Type == 'Link' and 'TextButton' or 'TextLabel'
		local Text = Instance.new(Type, Collection)
		Text.Name = string.format("%02i", Id) .. '_' .. Type
		Text.AutomaticSize = TextLabel.AutomaticSize
		Text.BackgroundColor3 = TextLabel.BackgroundColor3
		Text.BorderColor3 = TextLabel.BorderColor3
		Text.BorderMode = TextLabel.BorderMode
		Text.BorderSizePixel = TextLabel.BorderSizePixel
		Text.LayoutOrder = TextLabel.LayoutOrder
		Text.SizeConstraint = TextLabel.SizeConstraint
		Text.Visible = TextLabel.Visible
		Text.ZIndex = TextLabel.ZIndex
		Text.AutoLocalize = TextLabel.AutoLocalize
		Text.BackgroundTransparency = TextLabel.BackgroundTransparency
		Text.FontFace = TextLabel.FontFace
		Text.RichText = true
		Text.Text = Object.Text
		Text.TextColor3 = TextLabel.TextColor3
		Text.TextSize = TextLabel.TextSize
		Text.TextStrokeColor3 = TextLabel.TextStrokeColor3
		Text.TextStrokeTransparency = TextLabel.TextStrokeTransparency
		Text.TextTransparency = TextLabel.TextTransparency
		Text.TextTruncate = TextLabel.TextTruncate
		Text.TextXAlignment = TextLabel.TextXAlignment
		Text.TextYAlignment = TextLabel.TextYAlignment
		Text.Position = UDim2.new(0, Pos.X, 0, Pos.Y)
		
		if Text:IsA("TextButton") then Text:AddTag("CG_Button") end

		local Size
		local Text2 = ''
		repeat
			Size = Text.TextBounds
			Text.Size = UDim2.new(0, Size.X, 0, Size.Y)
			if Pos.X + Size.X > TextLabel.Parent.AbsoluteSize.X then -- whether text must be wrapped
				if Object.Type == 'Link' then
					Pos = Vector2.new(0, Pos.Y + Size.Y)
					--Size = Vector2.new(0, 0)
					Text.Position = UDim2.new(0, 0, 0, Pos.Y)
				else
					repeat
						local Char = string.sub(Text.Text, -1)
						Text.Text = string.sub(Text.Text, 1, string.len(Text.Text) - 1)
						Text2 = Char .. Text2
					until not Char or Char == ' '
				end
			elseif Text2 ~= '' then
				Text2 = Trim(Text2)
				table.insert(Sequence, Id + 1, {Type = 'Text', Text = Text2})
				Pos = Vector2.new(0, Pos.Y + Size.Y)
				Size = Vector2.new(0, 0)
				Text2 = ''
			end
		until Text2 == ''
		Pos += Vector2.new(Size.X, 0)
		if Type == 'TextButton' then
			Text.MouseEnter:Connect(function()
				Text.Text = '<u>' .. Text.Text .. '</u>'
			end)

			Text.MouseLeave:Connect(function()
				Text.Text = Text.Text:gsub("<u>", ""):gsub("</u>", "")
			end)

			Text.MouseButton1Down:Connect(function()
				require(workspace.GameManager.Data.GetData.LinkHref)[Object.Href](Arg)
			end)

		end
	end
	
	--TextLabel.TextTransparency = 0
	TextLabel:Destroy() -- remove the original TextLabel (with HTML Tags)
end

TextLabel text should be formatted as <a href="Link1" arg="Whateva">Click here</a>

1 Like