Replacing a whole webpage with alternative content (HTML + CSS + JS)

寵の児 提交于 2020-06-26 07:39:52

问题


I am facing a problem that I am sure is pretty common. I found many solutions to this problem, and they all have their pros and cons. I'll post what I found here (which I believe will be useful to others), and I hope you'll point me in the right direction.

Essentially, this is my scenario:

  1. I have a webpage in PHP: http://example.com/page_with_content_a_or_b.php.
  2. This page returns Content A when no POST parameters are specified, and Content B if there are.
  3. Assume a user connects to my page typing the previous URL in her browser (a GET request).
  4. My server returns the page with Content A.
  5. The user's web browser, via JavaScript, decides to replace Content A with Content B.

The question: How does the JavaScript replace the contents?

Well, as I've said, I've been looking for different solutions, but none seems perfect.

In order to discuss each possible solution, let me introduce you the resulting HTML code of each version:

HTML of Content A

<html>
  <head>
     <link rel="stylesheet" type="text/css" href="style_A.css">
     <script type="text/javascript" src="jquery.min.js"></script>
     <script type="text/javascript" src="gallery-animator.js"></script>
  </head>
  <body>
     <div class="gallery"><!-- images here --></div>
     <p>Content A</p>
     <script type="text/javascript" src="content-b-loader.js"></script>
  </body>
</html>

HTML of Content B

<html>
  <head>
     <link rel="stylesheet" type="text/css" href="style_B.css">
     <script type="text/javascript" src="jquery.min.js"></script>
     <script type="text/javascript" src="gallery-animator.js"></script>
  </head>
  <body>
     <div class="gallery"><!-- images here --></div>
     <p>Content B</p>
  </body>
</html>

Differences between both versions

As you can see, in the example both versions are quite similar, but not identical. In general, these are the differences I might encounter:

  • All or some or none imported stylesheets may be different.
  • All or some or none imported javascripts may be different.
  • There might be differences with inline stylesheets and/or javascripts.
  • The content is different, but it may differ a little bit only or be completely different.
  • Content B does not includes the script for loading itself (last script in Content A).

Possible Solutions

Replacing Content with document.open(), document.write(), and document.close()

The first solution I implemented is the following:

content-b-loader.js (option 1)

$(document).ready(function() {
    $.ajax({
        type:  'POST',
        url:   window.location.href,
        data: { load_content_b: 'true' },
        success: function( html ) {
            document.open();
            document.write(html);
            document.close();
        }
    });
});

Apparently, the solution works properly. However, there are situations in which I have problems. In my example, for instance, both contents load a script named gallery-animator.js. Assuming this script is the following:

gallery-animator.js

var galleryInterval = setInterval(function() {
   // Fade out current image and fade in the next one
   $("body > div.gallery > img")...
}, 5000);

after executing the script in content-b-loader.js there are two timeouts animating the gallery. As a result, the animation looks like a mess (two images moving at the same time, or not working at all).

It looks like the sequence document.open(), document.write(html), and document.close() does not stop and replace the original scripts.

Redirecting with POST data (using a form)

Another solution is doing a redirection described in this previous question. The solution works like a charm: on the one hand, I load the URL I need to load with the required POST data, which means I'll get Content B, and on the other hand, Content B is loaded in a "new page", which means that the scripts loaded by Content A are no longer there.

The problem here? If I refresh the page with Content B, I get a warning stating that "POST data is about to be resubmitted". This is undesirable, because it looks confusing for the user (I don't want her to know she had been redirected to a new page).

I know there's a solution called PRG (Post-Redirect-Get) which avoids this particular issue. However, it requires Content B to be accessible using a GET request (using GET params or COOKIES, neither of which I can use).

Using iframes

The last solution I've found is also interesting. Basically, it hides (or empties) the body from Content A, adds an iframe in the page, and loads Content B inside it:

content-b-loader.js (option 3)

$(document).ready(function() {
    $.ajax({
        type:  'POST',
        url:   window.location.href,
        data: { load_content_b: 'true' },
        success: function( html ) {
            $("body").css('', ''); // Remove all paddings and margins
            $("body").empty();
            $("body")append('<iframe id="content" seamless="seamless"' +
                'src="anchor.html"></iframe>');
            // Initialize vars in anchor.html and call the redirect functions
            document.getElementById('content').contentWindow.url = window.location.href;
            document.getElementById('content').contentWindow.options = {
                load_content_b: 'true'
            };
            document.getElementById('content').contentWindow.redirect();
        }
    });
});

anchor.html

This page implements the second solution (it uses a POST redirect).

<html>
    <head>
        <script type="text/javascript" src="jquery.min.js"></script>
        <script type="text/javascript" src="jquery.redirect.min.js"></script>
        <script type="text/javascript">
            var url;
            var options;
            function redirect(){
                $().redirect(url, options, 'POST');
            }
        </script>
    </head>
    <body></body>
</html>

By making the iframe as big as the window view, I can show the alternative content. If the user refreshes the webpage, the page that is refreshed is the "container" page (the one that originally had Content A), so no warnings appear at all.

The problems I am facing with this solution, however, are:

  • Users can disable iframes. How do I detect whether iframes are enabled or disabled?
  • When the user clicks a link in the frame (or submits a form), the "new page" is opened inside the iframe. This is not what I want: I want it to be opened in the main window. How do I do this? I know that there is the base directive for links... but what about forms? And JavaScript code that performs a redirection?
  • Does everything work properly inside iframes? Responsive themes, javascripts, ... As far as I can tell, they do, but I ignore whether users can limit what iframes can do.

TL;DR - Conclusions

I have a page http://example.com/page_with_content_a_or_b.php. The page returns Content A when accessed using a GET request, and returns Content B when accessed using POST. When user types the URL, she gets Content A, and Content B is loaded using JavaScript.

All solutions entail problems:

  • With document.open(), document.write(), document.close(), scripts get messed.
  • With POST redirections, refreshing the page popups a warning
  • With iframes, they are not always available, and I am somehow "stucked" inside them.

Any ideas? Am I missing something? Is there a preferred way to do what I'm trying to do?

Thank you very much!


回答1:


There's a hacky fix for the problem with messy script behavior in your first solution. Assuming that the script-related problem only occurs when using timeouts (I don't know if there are other scenarios in which things can go wrong....), there's an easy way to clear all timeouts. Just add this code:

var id = window.setTimeout(function() {}, 0);
while (id--)
    window.clearTimeout(id);



回答2:


A POST request should do things which change or affect data. Thus it makes sense to prevent the user from refreshing and that should be desirable behavior, I am confused whether you're doing an actual post request or otherwise why is redirecting to a POST page is so problematic?

Either way, regarding your <iframe> solution, let me give some suggestions. "Disabling iframes" is indeed technically possible on some browsers, but to do such a thing you have to either dive in about:config or very deep in the IE menu and it's entirely impossible on Chrome by default. So yeah, truth be said, I would not concern myself with that whatsoever, just like nowadays most sites do not concern themselves with users who disable javascript (any user with disabled iframes will probably have javascript disabled as well btw), simply because it's too rare to concern yourself with and it's one of those choices where you have to face the consequences yourself. Now, I was planning on directing you to the <base> tag as well, just realizing now that you already mentioned it in your post. Either way, the tag will work for forms as well, but not for javascript 'links'.

All in all I would advice you to rethink whether the page should be POST, if so, then you should just go with the warning and otherwise rework it to be just another GET page.




回答3:


I guess I'm missing something, because I don't clearly understand what do you mean by "The user's web browser, via JavaScript, decides to replace Content A with Content B", but:

If you know in advance the parameters of the elements whose content you'd like to replace, wouldn't a simple case by case replacement work?

<html>
  <head>
     <link rel="stylesheet" type="text/css" href="style_A.css">
  </head>
  <body>
     <div class="gallery"><!-- images here --></div>
     <p class="content">Content A</p>
  </body>

  <!-- This could be content-b-loader.js -->
  <script>
    var $stylesheet = document.getElementsByTagName('link')[0];
        $content = document.querySelectorAll('.content')[0];

    if ( $stylesheet && $content ) {
      if ( $stylesheet.getAttribute('href') == 'style_A.css'  ) {
        $stylesheet.setAttribute('href', 'style_B.css');
      }

      $content.innerHTML = 'Content B';
    }
  </script>
</html>


来源:https://stackoverflow.com/questions/22478441/replacing-a-whole-webpage-with-alternative-content-html-css-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!