1. Advertising
    y u no do it?

    Advertising (learn more)

    Advertise virtually anything here, with CPM banner ads, CPM email ads and CPC contextual links. You can target relevant areas of the site and show ads based on geographical location of the user if you wish.

    Starts at just $1 per CPM or $0.10 per CPC.

Hyperlink cannot be clicked while jQuery script is running

Discussion in 'jQuery' started by xms, Mar 6, 2017.

  1. #1
    I'm using Internet Explorer 11 and Mozilla Firefox 51.0.1. Why cannot I click a hyperlink while jQuery script is running? I run the code every 5 seconds. The code will replace the HTML code of a div element. The HTML code has hundreds of rows, and this all takes several seconds. The link can be clicked before and after but not while running the script.

    $(document).ready(function() {
      get_bio_data = function() {
        var bio = 106;
    
        jQuery.post("get_bio.php", {
          bio: bio
        }).done(function(data) {
          $('#bio').html(data);
        });
      };
    
      setInterval(get_bio_data, 5000);
    });
    Code (JavaScript):
    And here is the HTML code:

    <div id="bio">
      <a href="go.html">Link 1</a>, <a href="yo.html">link 2</a>,
      and other HTML code, hundreds of rows...
    </div>
    HTML:
    What to do?
    SEMrush
     
    Solved! View solution.
    xms, Mar 6, 2017 IP
    SEMrush
  2. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,630
    Likes Received:
    724
    Best Answers:
    152
    Trophy Points:
    470
    #2
    Either move the links outside of the container-div that gets updated, or rethink how you're doing the update/refresh - because as it stands now, those links shouldn't even be there unless they're part of the data you're fetching - if they are, you're shit out of luck, because it takes the time it takes, and the rewrite of the DOM isn't done until the whole code has been refreshed. If the links are permanent, just move them outside the container, or create another inner-container for the updated HTML.
     
    PoPSiCLe, Mar 6, 2017 IP
  3. deathshadow

    deathshadow Acclaimed Member

    Messages:
    8,927
    Likes Received:
    1,625
    Best Answers:
    232
    Trophy Points:
    515
    #3
    If we set aside that you're relying on the fat bloated slow train wreck of developer ineptitude that is jQuery, the remainder of your problem is that your passing HTML from the server instead of just the DATA, and then using innerHTML by way of jQuery's idiotic mouth-breathingly dumbass ignorant bullshit "$.HTML" wrapper.

    Every time you use innerHTML or worse, jQuery's pointless dumbass garbage wrapper for it the browser has to pass the ENTIRE DOM into a reverse parser just to rebuild the ENTIRE document so the markup can be inserted whilst retaining any other changes JavaScript has made as well as to parse any new openings/closings that might be added, to then reparse that new HTML into a new DOM which then has to be rendered. This is a half dozen or more steps the browser has to take that basically sends it off to never-never-land.

    Which is why the moment you see anything like this:

    document.getElementById('bio').innerHTML = x.responseText;

    or its malfing jQuery asshat pointlessly redundant equivalent.

    $('#bio').html(data);

    You can guarantee that 1) it's going to be slow and prevent page-wide navigation during the update... 2) the person who wrote it probably doesn't know enough about HTML, CSS, or JavaScript to be writing JavaScript, and 3) the server-side code is no winner either since it's slopping out markup instead of just sending the data in a more reasonable format like CSV, JSON, or even XML.

    What SHOULD probably be done is building your new markup using the DOM, bypassing the parsing stage altogether -- but that means rewriting whatever it is the server-side code is slopping out.

    I'm not sure jQuery can pull this off, but if you were working in vanilla JavaScript you MIGHT be able to avoid SOME of the problems by creating a new DIV, adding the data to it via innerHTML, then doing a parentNode.replace to swap the DIV entirely. This lets the parser work on the DIV off the live DOM, meaning the entire document doesn't have to be reparsed and the doc stays live until the final swap. It's not the PROPER fix, but it might be enough to do it.

    Assuming a PROPER XMLHttpRequest or ActiveX equivalent is in place with the parameter to the callback normalized, the vanilla.js for that would go something like:

    
    function xBio400(x) {
    	var
    		oldBio = document.getElementById('bio'),
    		newBio = document.createElement('div');
    	newBio.id = 'bio';
    	newBio.innerHTML = x.responseText;
    	oldBio.parentNode.replaceChild(newBio, oldBio);
    }
    
    Code (markup):
    By doing the innerHTML before we put it onto the live DOM replacing the orignal DIV entirely, we avoid making the browser have to parse the entire DOM.

    There is also the problem that you are setting an interval to be called regularly, which means you COULD end up having events stack. NEVER set a AJAX request to a interval, only set a new timeout once you KNOW the existing one has finished! It takes more than that five seconds, now you have multiple requests in the pipe stacking atop each-other to run "when possible" until you're basically DDOS'ing yourself!

    If you were using my elementals.js library, the full rewrite of what you are doing would read something like this:

    
    _.eventAdd(window, 'load', function() {
    	var oldBio = _d.getElementById(bioDiv);
    	function getBioData() {
    		_.ajax({
    			status : {
    				'200' : function(x) {
    					var newBio = _.make('#bio', { innerHTML : x.ResponseText });
    					oldBio.parentNode.replaceChild(newBio, oldBio);
    					oldBio = newBio;
    					setTimeout(getBioData, 5000);
    				}
    			},
    			request : { 'post', 'get_bio.php', 'bio=106' }
    		});
    	}
    });
    
    Code (markup):
    Though that gives me an idea for a new placement attribute type.... 'replace'.

    Even so, how you are passing data (as finished HTML) and how you are using a constant interval are what's really at fault here. You really should be passing JUST the data, not markup, and then building on the DOM using document.createElement and document.createTextNode.
     
    deathshadow, Mar 8, 2017 IP
  4. xms

    xms Active Member

    Messages:
    169
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    53
    #4
    Thanks a lot, deathsdhadow!
     
    xms, Mar 8, 2017 IP
  5. #5
    Oh, one other thing -- notice how I track "oldBio" in a variable where I only grab hold of the old one ONCE using getElementById, then inside the loop just use it?

    getElementById is slow... often painfully so if your DOM is big and filled with lots of ID's... Element.querySelector / Element.querySelectorAll -- and by extension jQuery's $(selector) method -- is even slower. Using either of those techniques to grab an element in realtime can introduce very, VERY large delays into your code with corresponding spikes in CPU use.

    In jQuery's case it -- along with .HTML() -- is one of the examples of what I mean whenever I talk about jQuery's not just gross inefficiencies, but actually actively encouraging bad development habits. The CORE of how jQuery does, well... EVERYTHING... its $(selector) method that you start almost every operation with, is a massive gross inefficiency that should be avoided whenever possible! MORE SO if you care about legacy support as the polyfill they use for QuerySelectorAll can be agonizingly slow.

    Even my own implementation in elementals.js is slow, which is why when possible you would only use it once at startup, not every single time you have an interval. Doing a getElement(s)By___, Element.querySelector(all), $() or _.query(all) really should be avoided "inside the loop".

    Basically what I'm saying is that this:
    $('#bio')

    Is a sloppy half-assed technique that really shouldn't be encouraged for use in JavaScript "inside the loop" -- and it's the core of how jQuery even does 90%+ of what people do in it.

    Which is why even if you were to use jQuery's AJAX handler, I'd probably still vanilla most of it:

    
    $(document).ready(function() {
    	function get_bio_data() {
    		var
    			bio = 106,
    			oldBio = document.getElementById('bio');
    
    		jQuery.post("get_bio.php", {
    			bio: bio
    		}).done(function(data) {
    			var newBio = document.createElement('div');
    			newBio.innerHTML = data;
    			newBio.id = 'bio';
    			oldBio.parentNode.replaceChild(newBio, oldBio);
    			oldBio = newBio;
    			setTimeout(get_bio_data, 5000);
    		});
    	};
    	get_bio_data();
    });
    Code (markup):
     
    Last edited: Mar 8, 2017
    deathshadow, Mar 8, 2017 IP
  6. xms

    xms Active Member

    Messages:
    169
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    53
    #6
    Thanks again, deathsdhadow! How would you modify the latest code if there was a HTML page with 50-100 divs with id's to be modified by the same way?
     
    Last edited: Mar 9, 2017
    xms, Mar 9, 2017 IP
  7. deathshadow

    deathshadow Acclaimed Member

    Messages:
    8,927
    Likes Received:
    1,625
    Best Answers:
    232
    Trophy Points:
    515
    #7
    I would rip the JavaScript out by the balls and do a pageload. That many div and that many requests have ZERO damned business on a WEBSITE.

    That or I would send the data as a single request with proper server side formatting (ASCII delimit, CSV, JSON, XML) and build it using the DOM instead of innerHTML. That way you are only sending the data, not the markup. To that end XML would be a poor choice if not for the fact that AJAX already parses it into a XMLDom you can navigate and operate upon.

    It MIGHT also be beneficial to instead switch to websockets and instead of constantly polling, have the server do a push so that only MODIFIED data is sent client-side. That's far more complex, but given the amount of data it sounds like you are talking (aka too much of it for one page) it would reduce the bandwidth consumed and amount of work the server would have to handle.

    THOUGH -- that's me guessing wildly and talking with my instincts. Not actually knowing what your data is or how it's marked up, I'm basically using the Ouija board to answer you.

    Assuming all those DIV are relatively uniform in the content being added, If I could see a sample of what's being sent from the server for one of them -- as in an ACTUAL sample of REAL data -- I could probably dial in a better answer for you.
     
    deathshadow, Mar 9, 2017 IP
  8. xms

    xms Active Member

    Messages:
    169
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    53
    #8
    Thanks! I was just wondering if it is possible to create some kind of loop if there is more than one div. So, let's say there are 5 divs on my HTML page.
     
    xms, Mar 9, 2017 IP
  9. deathshadow

    deathshadow Acclaimed Member

    Messages:
    8,927
    Likes Received:
    1,625
    Best Answers:
    232
    Trophy Points:
    515
    #9
    I would try to do it as a single AJAX request, and then loop the result to plug it in where appropriate... again at that point using the DOM and not innerHTML style methodology.

    You can however run multiple AJAX requests simultaneously, with different result handlers for each -- but that unnecessarily increases your server load thanks to being more connections. It can also trigger more parsing instances and more renders spiking CPU use for no good reason.

    It would really come down to how much control you have over what the server you're pulling data from is outputting. There's an old saying about CSS where the stylesheet is only as good as the HTML it is applied to -- in that same way an AJAX call is only as good as what the server is feeding it. The less control you have over the server output and the larger that server output is for no reason (like building the result as HTML), the less efficient your code will be client side.

    Sucks, huh?

    There are also times where it's a bit like assembly language -- where often "unrolling the loop" and using more code is actually faster. In that way manually building the DOM and plugging values in can be a LOT more JavaScript to write (though creating job specific helper functions can reduce that), but it can mean less data the AJAX call passes around meaning less overall bandwidth and simpler code server-side.

    ... and one of the whole reasons to use AJAX is to offload work client-side.
     
    deathshadow, Mar 9, 2017 IP
  10. xms

    xms Active Member

    Messages:
    169
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    53
    #10
    Thanks, great information! I'm just learning jQuery and I always have avoided using JavaScript if possible, but now I really have to use it. I will read all your replies once again, think about your words, and try coding again. Thanks again!

    All the best,
    xms
     
    xms, Mar 9, 2017 IP