I am trying to do all dom manipulations off screen and then make it visible. Which works, except now I have the situation where I am trying to do it with a form which I want to focus on the first input text upon rendering it on the browser.
Something like: myForm.prependTo(myDiv).show().find('input:first').focus();
Problem is that the focus is being called before the form has finished rendering which is causing the lovely error 'Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus'
How do other web developers handle the similiar situation of manipulating elements off screen and then making it visible? I wish jQuery had something like myForm.prependTo(myDiv, function() { /* on render code here */ })
I know one way of doing it is setting a timeout and when it fires I put focus on the input, but I feel like that's not really the cleanest way to do things. I know the iframe has an onload event, so I'm curious if people usually draw their elements in some hidden iframe and listen for its load event to know when the element has finished rendering? If so could you point me to an example of doing this?
myForm.prependTo(myDiv).show(function(e){
$(this).find('input:first').focus();
});
I know I'm 7 years late, but I had a similar problem, which I solved by putting the stuff I needed to happen after the render in a ready handler.
I had a restore function that worked, but there was zero or near zero visual feedback that the element had been restored.
I tried emptying the element first. It still worked, but still had zero visual feedback.
$("#someSelector").empty();
restore();
Then I discovered ready() happens after the rendering; so I changed it to something like....
$("#someSelector").empty().ready(function() {
restore();
});
Now the restore() doesn't happen until after the empty() action RENDERS. This means my element APPEARS to empty out and then refill (it always did, but now the user can see it happen).
I found this solution somehow a few days ago for a different problem with some vague search that I can't remember. Then I needed it again but couldn't exactly remember what I did. Now my searches included the word "jquery" and "render" and lead me here.
I ended up going thru my code to find out what I did, and I thought it might be a good idea to post it here in case other people stumble on this post and actually need to execute something AFTER rendering happens.
Cheers.
Related
Is there a way to get notified, after inserting an element into the DOM with insertBefore(), when this element becomes actually visible/available to user ? Especially to start applying CSS transforms on it ?
Complete problem
Forgive me if this question is recurrent, I didn't find a suitable answer so far. I'm trying to implement a custom popup dialog system on a website of my own, similar to SweetAlert or some other products.
I would like to apply some special effects when this popup shows up, such as a progressive darkening of the background, as well as a slow vertical motion on the box itself.
To achieve all of this, I spawn one big, fixed div element covering the whole screen (the background) and containing the popup box. When I need it, I first insert this element as body's first child, tagging it with a special invisible class. Once inserted, I remove this invisible class from the element and let the CSS rules do the magic.
The problem is that even if removed the class after having inserted this element, this one will be rendered only when the Javascript function leaves, hence directly in its final state.
When doing this on a complete initial page, the load event helps. I now would like to do the same on an existing page.
As always, I'm interested on both solutions to this (potentially XY) problem: if there's a better way to do it, I'll be happy to discover it, but I'm still interested in solving this particular situation anyway.
Thanks in advance to everyone.
EDIT: currently performing tests on Firefox 82.0.2
Thanks to comments above, here's a valid solution to both exposed problems:
"Mutation Observer", as well as former "Mutation Events" (now deprecated) are the best way to get notified when something is inserted. It won't help with animations issues, though, because it's still not guaranteed to be rendered yet at this time ;
Rather than applying a class then another to perform a transition, it's better to define a regular animation using #keyframes that plays only once. It's guaranteed to be played when the object appears, by definition.
Many thanks to "Pomax", F4st3r and epascarello for their help.
I have a page with bunch of 3rd party JS scripts. When I load the page, it scrolls down to a specific div.
I already spent 2 hours trying to find out which code is causing the scroll.
Is there a way to find out which script / part of the code is triggering the scroll?
Wow, was this hard to debug. Seems like the debugger has some missing features, like tracking the emitter of an event.
The problem is WooCommerce. Specifically, it appears that WooCommerce is setting autofocus on the billing_last_name input field. The browser is then automatically scrolling the page to bring the field into view.
One would hope that there is a configuration option to turn off autofocus, but it appears WooCommerce does not provide this.
You can try adding this to your theme
function disable_autofocus_firstname($fields) {
$fields['billing']['billing_first_name']['autofocus'] = false;
return $fields;
}
add_filter('woocommerce_checkout_fields', 'disable_autofocus_firstname');
If that doesn't work, you can create a CSS rule to hide the billing name field and then run a delayed JS function to show the billing name field after the page is fully loaded.
How I debugged it
Per the OP's request, and considering the bounty offered, I will describe how I debugged this.
I'm a little embarrassed that I didn't just say to myself "Hmm, the page scrolls up to a form, the cursor is in the first field of the form, I wonder if it has autofocus set." Unfortunately, I'm not mainly a front-end programmer, and autofocus did not come to mind at first.
I started with the idea that it was being scrolled via JavaScript, either an explicit call to a scroll function or by setting scrollTop on something. I put an event breakpoint on the scroll event and tried to determine where the scroll event was being generated. Although I found the scroll event, I did not find the source of it. All I could determine at this stage is that the scroll event was targeting the document, not something inside it.
I used monitorEvents to listen for events on document and found only 3, a click and 2 scrolls, the last of which was caused by a delayed scroll-to-top function inserted by the OP to work around the first scroll. I put an execution breakpoint on setting that timeout (not executing the function) in an attempt to "divide and conquer", that is, to see if the scroll was happening before or after that. I maintained that breakpoint for the rest of my debugging effort.
The weird thing was that generally, the page would not scroll before it hit that breakpoint, but sometimes it did. I thought that was odd, and although I didn't know what to make of it, it had me on the lookout for something unusual.
I tried searching all the JavaScript for "scroll" and "update" (text) to look for more breakpoints to set, and set a bunch at JavaScript that did scrolling, but nothing hit.
I noticed that there was a lot of JavaScript dynamically updating the page, and thought that maybe the scroll was due to an update of some sort.
I tried putting a jQuery event listener that logged all events on document (since the JS was using custom events not logged by monitorEvents, and I had already determined that document was the target of the scroll event) to emit all the events and see if it was some custom update event. There were a bunch of custom events, and I later generated the events in the console to see if the page would scroll in response. Since I could not get the page to scroll that way, I concluded that events were likely a dead end.
I switched tactics. I looked at where the page was scrolling to, and saw it was scrolling the WooCommerce form into place. So, while stopped at the execution breakpoint (described above), I deleted the entire WooCommerce form from the DOM, and verified that the page no longer scrolled. This had me convinced that whatever the problem was, it was caused by WooCommerce.
Unfortunately, my Google Fu failed me, and I did not immediately find the problem through a Google search. Instead I found how WooCommerce scrolls the page on errors to make sure the error messages are visible. This led me back to the JavaScript.
Still, there was a lot of JavaScript, a lot of it dynamically creating the form (localizing it on the fly), and a bunch of German (which I don't speak), and I wasn't finding any JavaScript causing scrolling. I really wanted to narrow down which JS file was causing the scroll.
Chrome allows you to set a breakpoint on "script first statement" (under Event Listener Breakpoints -> Scripts), so I did that. In addition to stopping at the first line in every script file, it stops at the beginning of every <script> tag on the page. I found this script tag near the bottom of the page
<script type="text/javascript">
if(typeof jQuery == 'undefined' || typeof jQuery.fn.on == 'undefined') {
document.write('<script src="https://www.prored3.de/wp-includes/js/jquery/jquery.js"><\/script>');
document.write('<script src="https://www.prored3.de/wp-includes/js/jquery/jquery-migrate.min.js"><\/script>');
}
</script>
The weird thing about this script tag was that the scroll happened immediately after this script tag was processed, but jQuery was already loaded, so the script actually did nothing. I was also able to confirm via the console that both before and after this script tag (which is before and after the scroll), the DOM was not flagged as ready. This means that all the jQuery ready handlers had not run by the time the scroll happened. That eliminates a lot of JavaScript, and got me thinking about why the scroll happened after but not before this tag.
I guessed that internally, the browser saw the document.write calls and determined that the DOM was not complete until after it passed that tag, but as soon as it was past it, the DOM was complete and it could start processing page-level attributes. That, along with the earlier observations, led me to look at the WooCommerce form more closely and discover the autofocus attribute set on the billing_first_name field.
Oddly enough, I was not able to prevent the scrolling by deleting the autofocus attribute. I don't know why, but I'm guessing it has to do with browser internals and the fact that the DOM was not ready. I was, however, able to prevent the scrolling by hiding the the billing_first_name in put element via CSS, which convinced me it was the cause of the scroll.
Adding "autofocus" to my Google search led me to other complaints of similar behavior with WooCommerce, and combining posts led me to the PHP solution I posted.
Updated
As I don't have OP's page for testing, the following method of finding registered event listener actually DO NOT solve the issue OP is addressing.
However, this is the general method when I want to find a specific event, just reserved for someone's reference.
If I understand your meaning correctly, you want a method to tell you where do the specific events occur. Please tell me if this is not doing what you want.
You can try to add a breakpoint on chrome debugger.
F12 -> Sources -> Event Listener Breakpoints (in list with those Breakpoints, Scope, etc) -> Control -> Click the box of scroll.
For Sure it may captures some other scroll event you are not interested, but you can go through it next by next until you find the one you want.
Besides, there may be also event not related to scroll, you may also need to try focus or DOM Mutation -> DOMFocusIn.
I've encountered an annoying issue while working on YUI.
I have a main area and a navigation block. The elements in the main area can be activated with a direct click or by clicking an element in the navigation block that triggers the appropriate element in the main area.
As it turns out, triggering a click event programmatically in YUI isn't as simple as I thought it might be. Looking at the documentation I found pleanty of information on how to attach and delegate events but not how to call one.
I found this question, but it deals with creating a new custom event and not calling an existing one.
All other similar questions are answered with using .simulate(), but this is actually not the best option for compatability reasons and it's also not recommended by YAHOO itself for client-side use http://yuilibrary.com/yui/docs/event/simulate.html#faking. EDIT: After re-reading the section I realized the warning is irrelevant for the subject of this question.
I found a solution by calling the click() command in the node's DOM element, but this is really a last resort and I would like to know if there's a more "clean" way to do it through YUI.
Here is an example of what I'm doing now: http://jsfiddle.net/3fso2dg8/
In the example, the second button is triggering the click event of the first button by using the DOM element
Y.one('#clickme')._node.click();
CONCLUSIONS
After more fiddling with the code I came to realize simulate() is the preferred option in most cases, but not all.
The YUI vesrion I'm required to work with (3.14) has a known issue on simulating a click event in IE9 and above. Since - for other technical reasons - I cannot upgrade to whatever version this issue was fixed and I need to keep a multi-platform compatibility, my original solution is still the best option. Anyone else that uses YUI components that don't respond well on IE, maybe you stumbled upon the same issue so this is one way to solve it.
After looking for exactly the same functionality I just used simulate in user-facing code - where It would just mimic clicking with no return method etc. (simple submit button or choose fil trigger).
When I would needed "complex" functionality I would just add a class or new ID and add new delegate or "on" method in my code - following the: "If a function needs to respond to user action or be called programmatically, it should be written accordingly and called directly in the latter case." prinsipp.
So to summarize - I use simulate for very simple effects with no callbacks or other "advanced" stuff and (sadly) duplicate other delegate/on elements where simulating would be tricky...
Had also looked into your method (._node.click();) and I can't see no obvious difference comparing to simulate()...
For a few years now I use user-JavaScript to put additional input buttons and clickable span-elements on pages. Usually I manage to make this work, e.g.
span = document.createElement("span");
span.onclick = __oujs.onClickAddPage;
span.appendChild(document.createTextNode("Add page"));
containingDiv.appendChild(span);
Usually __oujs.onClickAddPage() is called when I click on that span-element.
However, yesterday a site made some changes (apparently I have no clue what they were) that causes clicking on my elements to not cause any events. In the example above __oujs.onClickAddPage() is not called any more. The same is true for input-elements of type "button".
As I'm using Opera, DragonFly shows that my span still is the top-most element in that particular area and, therefore, it should handle the click-event. However, I understand that they include jQuery, which might be part of the misery.
Is there a special technique (maybe with a name that Google knows of) they use to able to do such thing? How do I get the control back and have my code called again? Can I remove some object?
I'm sorry for asking in a rather broad style, but I have no clue what I can look for to fix this myself. Please ask if you need to know something.
I would suggest you this steps:
Create the next function:
function stubFn(event){
console.log('event caught', event); // this will log the click event
__oujs.onClickAddPage.call(event.currentTarget, event); // emulate the onclick behavior
}
Use span.addEventListener('click', stubFn) to add the listener to the element in your code.
If it does not work, then you have to reverse-engineer the script and markup.
I'd suggest to check if there is any element with absolute or fixed position overlapping your span. It can prevent the event propagation.
In general, there are no ways to forbid the elements from userscripts to handle events using inlined handlers.
To get this off my open questions I answer this myself rather than waiting for it to be closed:
I'm sorry, it was my fault. I had a stupid mistake in another user-JavaScript file that affected all sites...
I reinstalled my browser and was thinking about reinstalling my OS, but luckily this isn't necessary.
So I have looked through most of the facebook questions here and it has absolutely confirmed my thoughts. Facebook development may be among some of the worst I've ever used. I'll avoid my rant for now, but as a note to where I'm coming from: tried php sdk, worked decently out of the box, found out I need to put the functionality on a pages tab, can't iframe on pages tab, have to use FBML (which they are retiring in two months, yet I can't start testing the iframe approach yet)
Anyway, I run into FBJS at this point. It works "good enough" for most things, but for some reason I can't get an event registered to an object (a select box in particular interest) by adding a listener (as per FBJS documentation). I am able to add it directly to the html object and have it work, but this is not desirable and I would really like to know what I consider the proper way to do it, which involves seperation of logic and display.
What I want to happen: click on the select box, make a selection, display the text of the selection in an empty div (later on adding Ajax but one step at a time here)
Code:
<script>
var obj = document.getElementById('select-id');
obj.addEventListener('onchange',my_func);
function my_func(evt){
var inner = document.getElementById('div-id');
inner.setTextValue('hey'); // for testing purposes
}
</script>
The above code doesn't do anything when I make a change to the select box. However, this behaves as planned:
<select name="find_state" id="find_state" onchange="my_func();">
I will be grudgingly using this method as I develop, but would really love to know what I might be doing wrong or if anyone else has this issue? And if anyone has any opinions on the matter I would love to know of some form of facebook development recommendations as applications, pages, and tabs all appear to behave totally different from eachother, yet it seems that they all should be doing the same thing to me? Is there any unified way to develop across all three of these things, or am I missing something?
Thanks in advance, as well as for the past help!
I think it should be:
obj.addEventListener('change',my_func);
(instead of onchange)
Straight from Facebook documentation:
The third parameter [to addEventListener], boolean useCapture is required (it does not have a default value)
That means that you should have:
obj.addEventListener('change', my_func, false);
Use the following html and your events attached with .addEventListener() start to work. This seems to be undocumented "feature".
<select name="find_state" id="find_state" onmousedown="return true;">
This also enables the event to fire first time the user changes the value of select. Otherwise it would fire only on second onchange event.