{"id":17621480,"url":"https://github.com/sql-mistermagoo/blazorwithoutinterop","last_synced_at":"2025-03-30T01:27:38.806Z","repository":{"id":91797741,"uuid":"180188170","full_name":"SQL-MisterMagoo/BlazorWithoutInterop","owner":"SQL-MisterMagoo","description":"Blazor Without Interop is an experiment in running dynamic JavaScript without JSInterop. Don't do this.","archived":false,"fork":false,"pushed_at":"2020-06-15T16:18:44.000Z","size":227,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-05T03:23:31.684Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SQL-MisterMagoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-08T16:20:46.000Z","updated_at":"2023-11-01T15:28:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"1f3cb677-5886-4e5d-a429-b0d2b5e1de98","html_url":"https://github.com/SQL-MisterMagoo/BlazorWithoutInterop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorWithoutInterop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorWithoutInterop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorWithoutInterop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorWithoutInterop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SQL-MisterMagoo","download_url":"https://codeload.github.com/SQL-MisterMagoo/BlazorWithoutInterop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246263971,"owners_count":20749379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-22T20:43:35.677Z","updated_at":"2025-03-30T01:27:38.472Z","avatar_url":"https://github.com/SQL-MisterMagoo.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Blazor Without Interop - BWI\n\nThis is a stupid test of a really stupid hack to run JavaScript from Blazor without using JSInterop.\nThere is no real purpose and probably no real use case.\nI am not promoting or endorsing this method, but I do like it ***just for fun***.\n\nChangelog:\n\n#### Version 0.1.0-beta-1\n- Initial commit contains a 3 project solution with four samples of BWI.\n1. Javascript test runner - enter and run any old JavaScript from the Blazor app.\n2. File Upload test - Hide the standard file input and use a custom button.\n3. Local Storage test - Read/Write local storage with inbuilt JSON serialisation.\n4. Form Validation test - Following up a blog post by @shawty an example of html5 form validation\n\n\nProjects in this repo:\n\n## BlazorWithoutInteropCore\n\nThis is the library that provides all the pages and components for the demo.\nEverything interesting is in this library.\n\n#### Sample components without JSInterop\n\n- JSRunner.razor - JS component\n- FileUploader.razor - Alternative file upload - triggered by your own button.\n- Storage.razor - Read/Write localStorage\n\n## BlazorWithoutInterop\n\nA Blazor/WASM front end for the demo - this is a shell only.\n\nUseful for testing everything works in WASM mode.\n\n## BlazorWithoutInteropServer\n\nA Blazor Server side front end for the demo - this is a shell only.\n\nUseful for debugging and making sure everything works server side.\n\n# What On Earth Did You Do?\n\nWell, I was bored and I wanted to run some JavaScript but didn't want the bother of wondering if I was Pre-Rendering or not.\n\nSo, I decided to find a way to trigger JavaScript without JSInterop and came up with the idea\n\"What if there was a way to render an html element and hook into it's lifecycle events to run some JS?\"\nI mean it's pretty standard practice to do things \"onload\" in the web world, but I wanted to be able to do this dynamically and **script** tags just didn't suit what I wanted.\n\nAfter a bit of research, I thought about `img` tags - maybe I could change the source of one of these and have that trigger something.\n\nSo, I tried it with two images, swapping between them and it worked - I could hook into the events for the image loading and run my JS snippets.\n\n```\n\u003cimg src=\"img1.png\" onload=\"console.log('it works!')\"/\u003e\n\n\u003cimg src=\"img2.png\" onload=\"console.log('it works!')\"/\u003e\n```\n\nBut that seems wasteful, loading images just to trigger a script, so I tried toggling an invalid src and hooking into the `onerror` event.\n\n```\n\u003cimg src=\"foo\" onerror=\"console.log('it works!')\"/\u003e\n\n\u003cimg src=\"bar\" onerror=\"console.log('it works!')\"/\u003e\n```\n\nThis sort of worked - in Chrome, but threw console errors as well - not nice.\n\nA bit more research and I found out that you don't get errors thrown if the URI is invalid, like `//:0`\n\n```\n\u003cimg src=\"//:0\" onerror=\"console.log('it works!')\"/\u003e\n```\n\nThe `onerror` event still fires, but it doesn't throw to the console!\n\nThen along came FireFox - Computer says No!\n\nA bit more fiddling and it turns out that FireFox doesn't trigger `onerror` but it does still fire `onloadstart`\n\nSo, now, I have this\n\n```\n\u003cimg src=\"@($\"//:0/{Src}\")\" onerror=\"@jsToRun\" onloadstart=\"@jsToRun\" hidden/\u003e\n```\n\nAll I have to do to run arbitrary javascript is set the Src to a unique string, set jsToRun to contain my JavaScript and render the component.\n\nYou will find the finished component in `Components/DragonsBeHere/JSRunner.razor`\n\nIn the final version, I added a hidden input that can receive a value back from the JavaScript.\n\n```\n\u003cinput id=\"@ReturnValueID\" type=\"text\" bind=\"@ReturnValue\" hidden/\u003e\n```\n\nThere is a method on the component to request a value from the script, which wraps your script in a little bit of code to get the return value, stores it in the hidden input and then triggers a change event on the input, so that Blazor's binding is triggered.\n\n```\npublic void GetValueJS(string js)\n{\n\tjsToRun = $\"{ReturnValueID}.value = eval('{js ?? jsToRun}');\" +\n\t\t$\"{ReturnValueID}.dispatchEvent(new Event('change'));\";\n\tSrc = DateTime.UtcNow.ToFileTime().ToString();\n\tTask.Delay(1);\n\tStateHasChanged();\n}\n```\n\nWhen the value is returned, Blazor binding updates the ReturnValue\n\n```\nstring ReturnValue\n{\n\tget =\u003e returnValue;\n\tset\n\t{\n\t\treturnValue = value;\n\t\tValueReturned?.Invoke(returnValue);\n\t}\n}\n```\n\nAnd there is an Action Parameter that you can bind to - this raises a notification that you have a result.\n\n```\n[Parameter] protected Action\u003cstring\u003e ValueReturned { get; set; }\n```\n\n### Summary\nFun times, hacking away on Blazor.\nDon't do this.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsql-mistermagoo%2Fblazorwithoutinterop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsql-mistermagoo%2Fblazorwithoutinterop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsql-mistermagoo%2Fblazorwithoutinterop/lists"}