Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing Links before jQuery is Loaded

How can I prevent links on click event before jQuery is loaded?
The reason is I have few links that make AJAX calls through jQuery ajax functions and if user click them before jQuery framework is loaded the browser cant trigger jQuery ajax function and will follow links href="...".

Edit: Can I use this?

<head>
...
  <script type="text/javascript">
   window.onload = prevCl();
   function prevCl() {
    var links = document.links[];
    links.onclick = check();
   }
   function check() {
    if (typeof jQuery == 'undefined') { 
     return false;
    }
   }
  </script>
</head>
like image 396
Ali Avatar asked Sep 28 '10 09:09

Ali


People also ask

What is jQuery noConflict()?

In jQuery's case, $ is just an alias for jQuery, so all the functionality is available without using $. Run $. noConflict() method to give control of the $ variable back to whichever library first implemented it. This helps us to make sure that jQuery doesn't conflict with the $ object of other libraries.

What is ready function in jQuery?

jQuery ready() MethodThe ready event occurs when the DOM (document object model) has been loaded. Because this event occurs after the document is ready, it is a good place to have all other jQuery events and functions. Like in the example above. The ready() method specifies what happens when a ready event occurs.

How to write ready function in jQuery?

$( document ). ready() ready() will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute. Code included inside $( window ). on( "load", function() { ... }) will run once the entire page (images or iframes), not just the DOM, is ready. // A $( document ).

Where should I put jQuery script?

Projects In JavaScript & JQuery It's always a good practice to add jQuery code in footer i.e. just before the closing </body> tag. If you have not done that, then use the defer attribute. The defer attribute is used to specify that the script execution occurs when the page loads.


2 Answers

Four options for you:

Option 0

Go read Sean Hogan's answer, he pointed out that this can be done with delegation (doh!) and I put together an example implementation.

Option 1

You can make the href of the links "#" until jQuery is loaded, and then (if appropriate) change it to what it really should be. You can store what the href should be in a data- attribute, e.g.:

<a href='javascript:;' data-href='realtarget.html'>blah</a>

Then:

jQuery(function($) {
    $("a[data-href]").each(function() {
        var $this = $(this);
        $this.attr("href", $this.attr("data-href"));
    });
});

Again, that last bit only if you really need to make the href an href. If you're handling the click via JavaScript, no need to.

If validation is a significant part of your development cycle, it's important to note that attributes in the form data-xyz are invalid in HTML4 and before (browsers don't actually care, but again, if you use validation...). They become valid as of HTML5.

Option 2

Use inline onclick attributes to intercept the click prior to jQuery loading and basically say "Sorry, one moment". So in a script tag at the top of your file:

function sorryLoading() {
    alert("Sorry, the page is still loading, one moment please.");
    return false;
}

...and then on the links:

<a href='realtarget.html' class='sorry' onclick='return sorryLoading();'>blah</a>

...then remove the onclick on jQuery load:

jQuery(function($) {
    $("a.sorry").removeClass('sorry').attr("onclick", "").click(yourRealClickHandler);
});

(You can leave the last bit — the click call — off if they don't all have the same click handler.)

I've used a class above to differentiate between these links and others that might have an inline onclick (otherwise, surprisingly the selector "a[onclick]" actually works on Chrome, Opera, and Firefox for Linux and IE6 and IE8 on Windows).

Option 3

Since it sounds like you want the page to be non-functional until jQuery loads, here's another approach:

  1. Make sure that the script tag for jQuery is in the head section of your page (not at the bottom, where I'd normally recommend putting it).
  2. At the very bottom of your page, just before closing the body tag, hook up your click handlers without wrapping them in a jQuery.ready call (or any of its shortcuts).

The reason is this: #1 will ensure that jQuery itself is loaded prior to the page being rendered, and #2 will hook up the handlers as soon as possible. Google recommends using a tag at the end of the page like that (rather than a ready function or similar) and says that at that point, the DOM elements will be ready to be discovered.


Separately, of course, you want to ensure that the time during which the links don't do what the user expects is absolutely as brief as possible by doing all of the page-load optimization stuff you can. Clicking a link and not having it do what it looks like it does makes for a poor user experience.

like image 110
T.J. Crowder Avatar answered Oct 15 '22 08:10

T.J. Crowder


You could use event-delegation: the DOM equivalent of jQuery live() and delegate() methods.

The following might work:

<head>

<script>
function linkMatcher(node) { // modify this if not all links are disabled
 if (node.href) return true;
 else return false;
}

document.onclick = function(e) {
 e = e || window.event;
 var target = e.target || e.srcElement;
 for (var node=target; node!=document; node=node.parentNode) {
  if (node.tagName == "A") return !linkMatcher(node);
 }
}
</script>
</head>

EDIT: This disables the links permanently. They can be re-enabled by removing the event-handler, e.g. document.onclick = null.


The complete, live example below works on Chrome, Opera, and Firefox for Linux as well as IE6 and IE8 for Windows. It prevents clicks on links with the class "wait" (you could do all links instead if you liked) and remembers the first link clicked. Then it simulates a long load delay (five seconds) and then hooks up jQuery handlers on the links and removes the document-level handler that was preventing clicks, and then triggers the jQuery handler on the link that was clicked (note that this only triggers the handler, not the underlying default behavior — but as you want to trigger your ajax stuff, I figure that's okay). — T.J. Crowder

HTML:

<!DOCTYPE html>
<html>
<head>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<meta charset=utf-8 />
<title>Link Click Delay Test Page</title>
<!--[if IE]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
  article, aside, figure, footer, header, hgroup, 
  menu, nav, section { display: block; }
  body {
    font-family: sans-serif;
  }
  p {
    margin: 0px;
  }
</style>
<script type='text/javascript'>
  // A scoping function to avoid creating global symbols
  (function() {
    // Figure out what to hook clicks on
    var container =
        document.body ||
        document.documentElement ||
        document;

    // Hook clicks that reach the bottom level
    hookClicks();
    function hookClicks() {
      if (container.attachEvent) {
        container.attachEvent("onclick", handleClick);
      }
      else if (document.addEventListener) {
        container.addEventListener("click", handleClick, false);
      }
    }

    // Set up an unhook function for jQuery to call
    window.unhookClicks = unhookClicks;
    function unhookClicks() {
      if (container.attachEvent) {
        container.detachEvent("onclick", handleClick);
      }
      else if (document.addEventListener) {
        container.removeEventListener("click", handleClick, false);
      }
    }

    // Handle clicks
    function handleClick(event) {
      var target;

      // Handle Microsoft vs. W3C event passing style
      event = event || window.event;

      // Get the target (again handling Microsoft vs. W3C style)
      target = event.target || event.srcElement;

      // Do we have a target that's an A with the class "wait"?
      if (target &&
          target.tagName.toUpperCase() === "A" &&
          (" " + target.className + " ").indexOf(" wait ") >= 0
         ) {
        // It's a link we want to prevent for the moment
        // Remember the element that was clicked if there
        // isn't already one (or replace it, depends on the
        // UX you want to provide
        if (!window.pendingLink) {
          window.pendingLink = target;
        }

        // Prevent it from being processed
        if (event.preventDefault) { // If W3C method...
          event.preventDefault();
        }

        // This should work if preventDefault doesn't
        return false;
      }
    }
  })();
</script>
</head>
<body>
  <p><a href='http://www.google.com' class='wait'>Google</a>
    - This one waits
  <br><a href='http://news.bbc.co.uk' class='wait'>BBC News</a>
    - This one also waits
  <br><a href='http://www.cnn.com'>CNN</a>
    - This one doesn't wait, it goes immediately
    </p>
</body>
</html>​

JavaScript (wherever your jQuery ready handler is):

jQuery(function($) {

  // Use setTimeout to fake a long delay loading
  setTimeout(function() {

    // Hook up our proper click handling
    // Obviously, replace this with whatever does the ajax
    $("a.wait").click(function(event) {
      alert("The jQuery handler for " + this.href + " link was triggered.");
    });

    // If we have clicks disabled, enable them
    if (window.unhookClicks) {
      window.unhookClicks();
      window.unhookClicks = undefined;
    }

    // Do we have a pending link to follow?
    if (window.pendingLink) {
      // Yes, grab it and clear it
      var $link = $(window.pendingLink);
      window.pendingLink = undefined;

      // Trigger any jQuery event handlers on it.
      // Note that this will NOT follow the link,
      // just trigger jQuery event handlers that
      // are on the link.
      $link.click();
    }

    // We've loaded!
    $("<p>Finally loaded!</p>").appendTo(document.body);

    // End of fake delay function
  }, 5000);

});​
like image 37
Sean Hogan Avatar answered Oct 15 '22 07:10

Sean Hogan