Remove Render-Blocking JavaScripts

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
8 years ago
Hi,

to avoid rendering blocking script I wrote a actionFilter. the filter put the inline js code and java sources to the bottom of each page and put the css files inline. Also the filter minize HTML, java and css. For minize javascript and css I used Microsoft Ajax Minifier. And this is the code:


//Filter Provider
public class NopFilterProvider : IFilterProvider
    {
        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {

          
            
            if (controllerContext.IsChildAction==false)
            {
                if (controllerContext.HttpContext.Request.HttpMethod == "GET")
                {
                    //rendering blocking script should not work for admin area
                    if (!actionDescriptor.ControllerDescriptor.ControllerType.FullName.Contains("Admin"))
                    {
                        
                          
                                return new[]
                            {
                                new Filter(new JavaFilterAttribute(), FilterScope.Action, null)
                            };
                            
                        

                    }
                }
                
            }
          


          


            return new Filter[] { };
        }
    }

Filter:

public class JavaFilterAttribute :ActionFilterAttribute
    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {

            filterContext.HttpContext.Response.Filter = new MoveScript(filterContext.HttpContext.Response.Filter);


        }


    }

MoveScript:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Text.RegularExpressions;
using System.Net;
using System.Web;
using Microsoft.Ajax.Utilities;

namespace Nop.Plugin.Misc.RC.Core.ActionFilter
{
    public class MoveFormScriptStream : Stream
    {

        



        StringBuilder s = new StringBuilder();
        StringBuilder shtml = new StringBuilder();
        StringBuilder js = new StringBuilder();
        StringBuilder css = new StringBuilder();
        public Stream baseStream;

        public MoveScript(Stream stream) :base()
        {
            baseStream = stream;
            
            

        }

        public override bool CanRead { get { return true; } }
        public override bool CanSeek { get { return true; } }
        public override bool CanWrite { get { return true; } }
        public override void Flush() { baseStream.Flush(); }
        public override long Length { get { return 0; } }
        public override long Position { get; set; }
        public override int Read(byte[] buffer, int offset, int count)
        {
            return baseStream.Read(buffer, offset, count);
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            return baseStream.Seek(offset, origin);
        }
        public override void SetLength(long value)
        {
            baseStream.SetLength(value);
        }
        public override void Close()
        {
            baseStream.Close();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            
            string HTML = Encoding.UTF8.GetString(buffer, offset, count);
            shtml.Append(HTML);
            HTML = shtml.ToString();

            //If Html document has body end tag
            if (HTML.Contains("</body>"))
            {

                
                

                //inlinescript with tag <script type="text/javascript">
                bool inlineScriptExist = HTML.Contains("<script type=\"text/javascript\">");

                while (inlineScriptExist)
                {

                    int startTag = HTML.IndexOf(@"<script type=");
                    int endTag = HTML.IndexOf(@"</script>", startTag);
                    string script = HTML.Substring(startTag, endTag - startTag+9);
                    string inlinescript = script.Replace("<script type=\"text/javascript\">", "").Replace("</script>", "");
                    s.Append(inlinescript);
                    HTML = HTML.Replace(script, "");
                    inlineScriptExist = HTML.Contains("<script type=\"text/javascript\">");
                }


                //inlinescript with tag <script>
                inlineScriptExist = HTML.Contains("<script>");

                while (inlineScriptExist)
                {

                    int startTag = HTML.IndexOf(@"<script>");
                    int endTag = HTML.IndexOf(@"</script>", startTag);
                    string script = HTML.Substring(startTag, endTag - startTag + 9);
                    string inlinescript = script.Replace("<script>", "").Replace("</script>", "");
                    s.Append(inlinescript);
                    HTML = HTML.Replace(script, "");
                    inlineScriptExist = HTML.Contains("<script>");
                }


                inlineScriptExist = HTML.Contains("<script src=");
                
                
                //put js script src to bottom
                while (inlineScriptExist)
                {

                    int startTag = HTML.IndexOf(@"<script src=");
                    int endTag = HTML.IndexOf(@"</script>", startTag);
                    string script = HTML.Substring(startTag, endTag - startTag + 9);

                    js.Append(script);
                    HTML = HTML.Replace(script, "");
                    inlineScriptExist = HTML.Contains("<script src=");
                }



                /// minify HTML
                HTML = Regex.Replace(HTML, @"[a-zA-Z]+#", "#");
                HTML = Regex.Replace(HTML, @"[\n\r]+\s*", string.Empty);
                HTML = Regex.Replace(HTML, @"\s+", " ");
                HTML = Regex.Replace(HTML, @"\s?([:,;{}])\s?", "$1");
                HTML = HTML.Replace(";}", "}");
                HTML = Regex.Replace(HTML, @"([\s:]0)(px|pt|%|em)", "$1");

                /// Remove comments
                HTML = Regex.Replace(HTML, @"/\*[\d\D]*?\*/", string.Empty);



                //minify js
                string _js = s.ToString();
                Minifier minifier = new Minifier();
                _js=minifier.MinifyJavaScript(_js);
              
                

                if (s.Length > 0)
                {
                    string test = s.ToString();
                    HTML = HTML.Replace("</body>", js.ToString()+"<script type='text/javascript'>" + _js + "</script></body>");
                }


                //css
                
                var matches = Regex.Matches(HTML, "<link href=\"(?<url>.*?)\" rel=\"(?<rel>.*?)\" type=\"(?<type>.*?)\" />(?<text>.*?)");
                int cssCount=0;
                foreach (Match m in matches)
                {
                    if (m.Groups["type"].Value.Contains("text/css"))
                    {
                        string url = m.Groups["url"].Value;
                        cssCount=cssCount+1;
                        string host = HttpContext.Current.Request.Url.Host;
                        string scheme = HttpContext.Current.Request.Url.Scheme + "://";
                        var webClient = new WebClient();
                        string readHtml = webClient.DownloadString(scheme + host + url);

                        readHtml = readHtml.Replace("../", "Themes/Tierarzt24/Content/");
                        css.Append(readHtml);
                        HTML = HTML.Replace(m.Value, "");
                        
                    }


                }

                string cssCompress = minifier.MinifyStyleSheet(css.ToString());

                HTML=HTML.Replace("<head>", "</title><style>" + cssCompress + "</style>");



                buffer = System.Text.Encoding.UTF8.GetBytes(HTML);



                baseStream.Write(buffer, 0, buffer.Length);

            }


            

        }

      

        

    }
}
8 years ago
Great work!

Can you share the plugin w/source with us?
8 years ago
Maybe Andrei can solve this issue on 3,70 instead of 3,80.

work item
8 years ago
Hi,

I gave the code to Andrei yesterday. And he put the files to the workItem. You can download the code from there. You have  to use these files and to register the filter. Also you have to install the AjaxMinify dll for minification of js and css.

Regards,

Dominik
8 years ago
Perfect. Now we are waiting for Andrei :)))
8 years ago
There is some issues in your code. We are working to improve it and will share with you again!
8 years ago
Yes, Sure.  I'm waiting for your improvements.
8 years ago
Beagle wrote:
Yes, Sure.  I'm waiting for your improvements.


We have finished the javascript part, now no more scripts block rendering.

The CSS part is more complicated, because you added all of them inline in page's head. In some cases, stores have big CSS files and google doesnt recommend add huge inline styles in page, this way, we have to get all CSS files and insert then at end of page, but it will break some important visual styles and page will load "empty" until css files are loaded.

Google recommends add important and critic styles inline in head, but each store owner should do itself. So, just changing nop CSS position to FOOT should solve the CSS render blocking issue.

We will add some verification just to certify there is no CSS files added in CSHTML as <link> will block rendering, as you already done, but add them as an CSS file at document's end.

Ivan.
8 years ago
Good news!

It is done and working great.

We got almost 100% on google page insights (some losses on compressing images and another items, but pretty much easier). We are on 95%.

Now we are making a lot of tests to ensure no problem can occurs.

I will get back to you in couple days, maybe after weekend!

Ivan.
8 years ago
It's sounds good. It would be nice if you share your imporovements. How did you solve the problem with huge css files?

Regards,

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