I've implemented a chat application using socket-io and nodejs. The application is running fine but, sometimes, I'm facing problems to treat HTML content because when I try to $('#id').hide() or $('#id').show() nothing happens because the element id is not available.
If I try to refresh page pressing F5, sometimes it works because that element is rendered before I try to hide or show it. I got this behavior while debugging using Google Developer tools but I'm not sure if it's the "real why".
I was trying to find on Internet what is the life cycle of DOM elements but I didn't find something that could clarify my thoughts.
I'm trying to reproduce this problem on development environment but I'm pretty far of reach the problem:
<script>
console.log('Creating socket');
var socket = io();
console.log('Socket created');
socket.on('connected', function (data) {
console.log('connected to server');
});
function timeout() {
setTimeout(function() {console.log('sleeping')}, 5000);
}
$(document).ready(function(){
timeout(); // is possible to stuck process on this point?
console.log('Ready');
});
</script>
No matter where I put socket.on('connected'.. because it's always called after console.log('Ready'). Based on this, my theory of F5 refresh is not correct and I feel that I'm running in circles.
Anyone have an idea why HTML elements are not present sometimes?
And, If I use socket.on('anyevent, {}) inside $(document).ready(function(){} do I have any guarantee that the event will only be processed after page being full rendered?
On a real world, all our sockets events are inside $(document).ready(function(){} but still not hiding or showing some html elements because they aren't present.
I am not sure about your HTML and code structure but this sounds like you are binding your event listeners to a dynamically added element but this element does not exist at the time of the binding.
If my understanding is correct, you need to add the binding on an element but base the action on the newly added element, something along the lines of:
// Add event listener, bound to the dynamically added element
$("button").on('click', $("#newElemId"), function(){
// if element exists, toggle it
if($("#newElemId").length){
$("#newElemId").toggle();
}else{
console.log('element not present yet');
}
});
See demo below:
$(function(){
// define function to add an element to the DOM
var addElement = function(){
var newElementAsObj = $(document.createElement('div'));
// add an id for querying later
newElementAsObj.attr('id', 'newElemId');
// add some text (so it's visible)
newElementAsObj.text('New Element');
$("#container").append(newElementAsObj);
console.log('new element added!');
}
// add a new element after a few secs
setTimeout( addElement, 5 * 1000); // time is in ms so 5*1000 = 5secs
// Add event listener, bound to the dynamically added element
$("button").on('click', $("#newElemId"), function(){
if($("#newElemId").length){
// if element exists, toggle it
$("#newElemId").toggle();
}else{
console.log('element not present yet');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<button>Toggle</button>
</div>
First, regarding this:
$(document).ready(function(){
timeout(); // is possible to stuck process on this point?
console.log('Ready');
});
No, it's not possible. But you don't need to wait there. You can remove that timeout function entirely.
You should move the socket.on('connected', function ... into $(document).ready(... because you don't want to respond to any socket events until the document is ready.
<script>
console.log('Creating socket');
var socket = io(); // It's fine to do this before the document loads
console.log('Socket created');
$(document).ready(function(){
socket.on('connected', function (data) {
console.log('connected to server');
});
console.log('waiting for connection...');
});
</script>
JQuery documentation describes that you can use $(window).on('load', function() {}) to run your code after the entire page, not just the DOM, is ready. You might try that if it's not enough that only your DOM is ready, but you need to wait for the whole page to be loaded.
https://learn.jquery.com/using-jquery-core/document-ready/
If I try to refresh page pressing F5, sometimes it works because that element is rendered before I try to hide or show it. I got this behavior while debugging using Google Developer tools but I'm not sure if it's the "real why".
Are you dynamically creating that element for example with $().append() or similar? If so, make sure that you actually create the element before you try to interact with it. If the element is not created dynamically, make sure that the code that interacts with the element is inside the $(document).ready or $(window).on('load') callback.
No matter where I put socket.on('connected'.. because it's always called after console.log('Ready'). Based on this, my theory of F5 refresh is not correct and I feel that I'm running in circles.
This happens because establishing the socket connection takes more time than to render the DOM. It's generally good idea to attach the event listener as soon as possible to not miss any events. If you attach the event listener only after the DOM has loaded, you might miss some events. But be aware, that if you manipulate the DOM inside the event listener callback, then you cannot be sure that the DOM is loaded and your target element is there, unless you attach the event listener after the DOM has loaded. So I recommend to attach event listeners after the DOM has loaded. At least those that contains some code to modify the DOM.
Anyone have an idea why HTML elements are not present sometimes?
There are not many possible reasons for this. Either your elements are not yet loaded or your code has removed them for some reason. I suggest putting breakpoints to the places where you create the elements or manipulate them somehow and see what the execution order is.
And, If I use socket.on('anyevent, {}) inside $(document).ready(function(){} do I have any guarantee that the event will only be processed after page being full rendered?
You have a guarantee that the callback function will be executed when the anyevent occurs and when the DOM is ready, that is, all the static html elements are there.
Related
I have two scripts.
The first script holds a prototype class of a game. This class is with use strict and isn't surrounded by document ready. The trigger:
$("#trigger").trigger("noPossibilities");
In the second script, which also has use strict, I try to catch the trigger:
$(document).ready(function() {
$("#trigger").on("noPossibilities", function() {
console.log("noPossibilities trigger");
});
});
The problem is that I can't catch the trigger. This has probaly something to do with use strict/scope but I can't seem to find a way around this.
Really hope someone can help me
UPDATE
The first script has a prototype class.
This class is getting instantiated in the second script. After the handler. Then it still doesn't work because the first script is loaded before the second script?
Also when I execute this line from the console:
$("#trigger").trigger("noPossibilities");
It doesn't get triggered. Shouldn't it work this way?
UPDATE 2
I found the problem. The first script adds the element with id trigger to the document when it is instantiated. I have a popup at the beginning of the game. Now the handler is getting attached on the click of that button.
The document probaly didn't have the element on which the handler should have gotten attached to. Now it is being attached later on and now it's working.
The issue is not with the scope, you are triggering the event before the handler is attaching to the element. The code inside document ready handler executes only after the DOM elements are loaded. In case you are triggering immediately after the script then it won't work since the elements are still loading.
You can check the working of triggering within a different context by moving it to another document ready handler(to execute only after handler attached).
$(document).ready(function() {
$("#trigger").on("noPossibilities", function() {
console.log("noPossibilities trigger");
});
});
$(document).ready(function() {
$("#trigger").trigger("noPossibilities");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="trigger"></div>
I'm trying to detect whether an element is visible after the DOM is ready. This element is a third party widget that loads slower than the page does.
My console.log for 'dom ready' is firing - like it should, but my check to see if the element is visible is returning false, as the element loads after the dom is ready.
Is there a way to listen for elements after the dom has loaded?
<script>
$(function(){
console.log('dom ready');
if($(element).is(':visible')){
console.log('element is visible. do stuff.');
}
});
</script>
you can get the id from the iframe, or from the document that is being loaded and do something like this..
$('#external').load(function(){
//lets do something when is loaded
if($(element).is(':visible')){
console.log('element is visible. do stuff.');
}
});
This will trigger once that script, iframe is done loading
Try to read a documentation, maybe third-party widget's API allows you to attach listener on create event. That would be an ideal solution.
If it is not possible try using setTimeout function:
$(function(){
console.log('dom ready');
setTimeout(function() {
if($(element).is(':visible')){
console.log('element is visible. do stuff.');
}
}, 10);
});
If 10 ms is not enough, you may increase this interval unless it works, although I don't recommend using this approach.
I have this bit of code that monitors clicks on <div class="selectable_item">
$(function(){
$("#matchres .selectable_item").on("click", function(){
console.log('Sending request')
$.post("/request", $.param({'crit_id': this.id}), function(){}).fail(function(){console.log("matchres error...");});
return true;});
});
What I'm noticing is when I use the chrome console, for example, to see if there are any $("#matchres .selectable_item"); it finds them, and if I define in the console $("#matchres .selectable_item").on("click", function(){console.log('hi')}); the action is as expected and the console logs correctly. But what I showed you above does not work. Any ideas why that is? Any help would be very much appreciated. As added information, I'm using jquery v1.10.2.
#Hanlet's idea is correct, at the time of document load those items don't exist because you're dynamically creating them, and they do exist by the time you interact with them in the developer console. What you want to do is bind the event handler to a delegate, or an object that will listen for events on child elements.
What you do not want to do is add delegate callbacks to the document when avoidable. Any click on the document will have to check against its event target to see if it should trigger this document delegate callback. You do this enough times and it becomes a performance concern. Instead, pick the closest ancestor element that isn't dynamically created.
For instance, if you're creating .selectable_item dynamically but not #matchres, then add this:
$('#matchres').on('click', '.selectable_item', function () { ... });
Because you add these dynamically, this could be an event delegation issue, very common. Try this instead:
$(document).on("click", "#matchres .selectable_item", function(){ ... }
The problem consists mainly in the fact that you bind these when the DOM is first built, and then you add more elements dynamically, but the event is not bound to these new elements.
Look at these two examples:
http://jsfiddle.net/hescano/aKfWf/
and
http://jsfiddle.net/hescano/aKfWf/1/
I use the following jquery in my page.
var j = jQuery.noConflict();
j(document).ready(function(){
console.log(j("#label_19"));
j("#label_19").on("click",function(){
alert("Clicked");
});
});
When document loads, the element (it's a checkbox) appears in the console. But when I click the checkbox, the alert is not thrown. But when I copy the same code (as below)
j("#label_19").on("click",function(){
alert("Clicked");
});
in console panel and press run. Now when I click the checkbox, the alert is thrown. What could be the issue in this case?
Updated:
What I observe in console is:
Object[input#label_19.document_label attribute value = "Check-In"]
The HTML markup is
<input id="label_19" class="document_label" type="checkbox" value="Check-In" name="label[19]">
The only explanation that fits the facts you've presented is that there is code running after your ready callback but before you click the element that replaces the element in question. Some kind of ajax callback or similar.
You'll need to look through your code to find the place where that's happening. Things to look for are any html calls on elements that contain the #label_19 element, or (if there's a mix of jQuery and non-jQuery code) assignments to innerHTML.
You can use event delegation to solve this, which may or may not be the best answer depending on what your code is doing. That looks like this:
var j = jQuery.noConflict();
j(document).ready(function(){
console.log(j("#label_19"));
j(document).on("click", "#label_19", function(){ // This is the changed line
alert("Clicked");
});
});
There, instead of hooking click on the actual element, we're hooking it on document but then asking jQuery to only tell us about clicks that pass through elements matching the selector we give it as they bubble. That way, the fact that something is destroying and recreating the #label_19 element doesn't matter, because we're not hooking a handler on that element. We're hooking the handler on document and checking, when the event occurs, if it passed through something that matches that selector.
But I wouldn't just blindly use event delegation, I'd find out what's really happening with that element.
Without seeing the rest of your code—including HTML and related DOM elements—have you considered using j(window).load() instead of j(document).ready()
var j = jQuery.noConflict();
j(window).load(function(){
console.log(j("#label_19"));
j("#label_19").on("click",function(){
alert("Clicked");
});
});
As explained here:
The window load event executes a bit later when the complete page is fully loaded, including all frames, objects and images. Therefore functions which concern images or other page contents should be placed in the load event for the window or the content tag itself.
I had a similar issue, it got resolved after i wrapped it in a window.load
(function ($) {
$(window).on('load', function () {
//MY click calls inside here
});
})(jQuery);
Hope it helps!
I want to attach a function to a jQuery element that fires whenever the element is added to the page.
I've tried the following, but it didn't work:
var el = jQuery('<h1>HI HI HI</H1>');
el.one('load', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
What I really want to do is to guarantee that another jQuery function that is expecting some #id to be at the page don't fail, so I want to call that function whenever my element is loaded in the page.
To clarify, I am passing the el element to another library (in this case it's a movie player but it could be anything else) and I want to know when the el element is being added to the page, whether its my movie player code that it is adding the element or anything else.
I want to attach a function to a
jQuery element that fires whenever the
element is added to the page.
You want the livequery plugin, which does just this. The recent live function is similar, except it won't call you when the element is added. We use it all the time-- works great.
You'll use $('h1').livequery(function() {alert('just added');});
I do not know that there is this type of event, what comes to mind is creating the event "el-load" based on this tutorial, and then extend "append" to know if the item has this event make the call to it.
Use LiveQuery (jQuery plugin), and attach a load event to ur dom element (h1), in this case.
try overwriting the append method so you can add your own event?
jQuery.extend(jQuery.fn, {
_append: jQuery.fn.append,
append: function(j) {
this._append(j);
j.trigger('append');
}
});
var el = jQuery('<h1>HI HI HI</H1>');
el.one('append', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
If the tag is being created via ajax, you can use a related node to subscribe to the ajaxSuccess event.
http://docs.jquery.com/Ajax/ajaxSuccess
$('#somenode').ajaxSuccess(function(){
if ($('h1').length > 0) { ... }
});
If it's just being added to the DOM by a local script, I'm not sure it's possible to observe it's creation, with the exception of using a timer to poll for it.
Depending upon the browsers you need to support there are DOMNodeInserted and DOMNodeInsertedIntoDocument events. Can't vouch for how well they work myself but theoretically you could bind to these events and then either check the new node and the possible subtree that was inserted, or just check the entire document again with a $(selector) of your choosing to detect the node you're waiting to see added.
Try these:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
setTimeout(function(){
window.alert('loaded');
},1000);//delay execution about 1 second
or to be safe this one:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
window.checker = setInterval(function(){
if($('someselector').length>0){ //check if loaded
window.alert('loaded');
clearInterval(window.checker);
}
},200);
basically, this will loop every 200ms until the selector gets result, and terminates the loop when the result is available