Is there a way to call a JavaScript function embedded inside an iFrame widget

Is it possible to embed an HTML script consisting of a JavaScript function inside iFrame widget and then call that function from another widget such as a button?

Hey Praveen!

Could you share a little more about your use case?

I need to build my own custom tool, so I used iFrame and embedded an HTML script. It is essentially a table, here is a short version for the script,

<html>
<head>
	<script>
		window.iframefunc = function(){
			return getPositionData();
		}
		function changeColor(e)
		{
			var color=e.options[e.selectedIndex].value;			
			e.style.backgroundColor=color;
		}
		function getColorIntegerEquivalent(colour)
		{
			if (colour == "red")
			{ return 1;}
			else if (colour == "yellow")
			{ return 2;}
			else 
			{ return 0;}
		}
		function getPositionData()
		{			
			let oTable = document.getElementById('PosTable');
			let data = [...oTable.rows].map(t => [...t.children].map(u => u.children[0])).slice(0,3);
			let colours = data.map(i => i.map(j => getColorIntegerEquivalent(j.style.backgroundColor)));
			return colours;
		}
	</script>
	<style>
		.grey{background-color:#d4d4d8;}
		.white{background-color:white;}
		.red{background-color:red;}
		.yellow{background-color:yellow;}
		select {-moz-appearance: none;
				-webkit-appearance: none;				
				padding: 1px 10px 10px 10px;}
		select::-ms-expand {display: none;
							width: 120%;}
		table, th, td {border: 1px solid black;
					   border-collapse: collapse;
					   padding: 0; 
					   margin: 0;}		
	</style>
</head>
<body>
<table id="PosTable">
  <tr>
    <td>
		<select onchange="changeColor(this)" style="background-color:#d4d4d8">
			<option selected value="#d4d4d8" class="grey"></option>		
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
    <td>
		<select onchange="changeColor(this)">			
			<option selected value="white" class="white"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
    <td>
		<select onchange="changeColor(this)" style="background-color:#d4d4d8">
			<option selected value="#d4d4d8" class="grey"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
	<td>
		<select onchange="changeColor(this)">			
			<option selected value="white" class="white"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
</tr>
</table>
</body>
</html>

I need to call the function getPositionData() to extract the integer representing the colour in the cells at the click of a button.

@Praveen we’re working on an option to post a message to an iFrame widget. Would that solve this for you?

1 Like

hi @Nikhil, I just succeeded in finding a workaround for this.
What I am doing here is I am embedding,
parent.postMessage(getCarrierPositionData(),"*"); to post a message and i get the message on the appsmith button using {{iframe.message}}

and to post the data back I am embedding the data as a variable, so now the code became,

<html>
<head>
	<script>
		var Posdata = {{Query.data}};
		function changeColor(e)
		{
			var color=e.options[e.selectedIndex].value;			
			e.style.backgroundColor=color;
			parent.postMessage(getPositionData(),"*");
		}
		function getIntegerToColourEquivalent(value)
		{
			if (value == 1)
			{ return "red";}
			else if (value == 2)
			{ return "yellow";}
		}
		function getColorIntegerEquivalent(colour)
		{
			if (colour == "red")
			{ return 1;}
			else if (colour == "yellow")
			{ return 2;}
			else 
			{ return 0;}
		}
		function getPositionData()
		{			
			let oTable = document.getElementById('PosTable');
			let data = [...oTable.rows].map(t => [...t.children].map(u => u.children[0])).slice(0,3);
			let colours = data.map(i => i.map(j => getColorIntegerEquivalent(j.style.backgroundColor)));
			return colours;
		}
		function setPositionData()
		{
			let oTable = document.getElementById('PosTable');
			let i = 0;
			for(let j=0; j<4; j++)
			{
				if(Posdata[j] != 0)
				{
					let element = oTable.rows[i].cells[j].children[0];
					element.style.backgroundColor = getIntegerToColourEquivalent(Posdata[j]);
				}
			}
			
		}
		window.onload = function(){
			setPositionData();
			parent.postMessage(getPositionData(),"*");			
		}
	</script>
	<style>
		.grey{background-color:#d4d4d8;}
		.white{background-color:white;}
		.red{background-color:red;}
		.yellow{background-color:yellow;}
		select {-moz-appearance: none;
				-webkit-appearance: none;				
				padding: 1px 10px 10px 10px;}
		select::-ms-expand {display: none;
							width: 120%;}
		table, th, td {border: 1px solid black;
					   border-collapse: collapse;
					   padding: 0; 
					   margin: 0;}		
	</style>
</head>
<body>
<table id="PosTable">
  <tr>
    <td>
		<select onchange="changeColor(this)" style="background-color:#d4d4d8">
			<option selected value="#d4d4d8" class="grey"></option>		
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
    <td>
		<select onchange="changeColor(this)">			
			<option selected value="white" class="white"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
    <td>
		<select onchange="changeColor(this)" style="background-color:#d4d4d8">
			<option selected value="#d4d4d8" class="grey"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
	<td>
		<select onchange="changeColor(this)">			
			<option selected value="white" class="white"></option>
			<option value="red" class="red" name="colors"></option>
			<option value="yellow" class="yellow" name="colors"></option>
		</select>
	</td>
</tr>
</table>
</body>
</html>

this has solved my problem, but i would recommend adding a capability to appsmith, to be able to call javascript functions that are embedded along with an HTML script inside an iframe.
This way people can have the flexibility to make their own custom widgets by creating html scripts and be able to manipulate it by calling javascript functions.

Hey @Praveen, we are working on a window.postMessage feature which will help you out with this issue. It’s still in the dev stage, once in you will be able to implement your use-case!