I need to add an HTML5 video to a page with the following code:
document.querySelector("#mydiv").appendChild(video);
console.debug(document.querySelector("video").clientWidth);
My problem is, the second line returns 0 because it takes a while for the video tag to load the video it's going to play. If I manually type the second line in the console once the page is loaded, I get the actual value. Is there a better way to get this value than using timeouts to periodically check if the value's been updated?
I needed to do something similar and stumbled upon this question, as I though maybe there is a callback for this. Turns out there is not. So here is another way of doing it using Window.requestAnimationFrame().
It would look something like this:
// Prepare the new element
let video = document.createElement('video');
// Add the element to the DOM. Works with any element
document.querySelector("#mydiv").appendChild(video);
// Updateing the DOM will cause a re-render, which we can hook into
window.requestAnimationFrame(() => {
console.debug(document.querySelector("video").clientWidth);
});
<div id="mydiv">Video goes here:</div>
Check the console to see that it works.
Javascript is one-threaded.
You should to drop your code to event-loop, this allows the js with DOM work.
Just try followed:
document.querySelector("#mydiv").appendChild(video);
setTimeout(function(){
console.debug(document.querySelector("video").clientWidth);
});
Add a video.onloadstart event to the element.
video.onloadstart(getClientWidth);
document.querySelector("#mydiv").appendChild(video);
function getClientWidth() {
console.debug(document.querySelector("video").clientWidth);
}
Read more here.
Related
I have a website running with Wordpress and using some extensions and plugins.
There is a button added through an extension, and I try to get this element with getElementById in javascript code added in the header of the page. But even inside a document.onload, the getElementById returns null.
I guess that's because the element is added dynamically after the page is loaded and even after the document.onload statment.
How could I manage to get the element in javascript?
Thanks for helping.
You might want to use a timer for this.
var myTimeInterval = setInterval(function() {
var el = document.getElementById("element");
if (el) {
// Your code here
clearInterval(myTimeInterval);
}
}, 1000);
This one will check every second whether #element exists or not, if it exists, perform your actions and remove the timer, if not, skip to the next iteration.
Try window.onload
http://javascript.tutorialhorizon.com/2016/03/17/window-onload-vs-document-onload/
A dirty solution may be to set a timeout before you try to select the element to give it time to load.
I've created this code to print data from an iFrame
function (data) {
var frame = $("<iframe>", {
name: "iFrame",
class: "printFrame"
});
frame.appendTo("body");
var head = $("<head></head>");
_.each($("head link[rel=stylesheet]"), function (link) {
var csslink = $("<link/>", { rel: "stylesheet", href: $(link).prop("href") })
head.append(csslink);
;});
frame.contents().find("head")
.replaceWith(head);
frame.contents().find("body")
.append(this.html());
window.frames["iFrame"].focus();
window.frames["iFrame"].print();
}
This creates an iFrame, adds a head to where it sets all the css links that are needed for this website. Then it creates the body.
Trouble is, the styling won't get applied to the print, unless I break at line frame.contents().find("head").replaceWith(head), which means that something in that part is running asynchronously.
Question is, can I somehow get the code to wait for a short while before running that line, or is there perhaps another way to do this? Unfortunately I'm not all that familiar with iFrames, so I have no clue what it's trying to do there.
This turned out to be a real hassle. I've always been reluctant to using iframes, but since there are many resources saying that using an iframe for printing more stuff than what's on the screen, we figured we'd give it a try.
This was instead solved by putting the data inside a hidden div which then was shown before a window.print(). At the same time, all other elements on the page were given a "hidden-print" class which is a class we're already using to hide elements for prints.
This might not be as elegant for the user (The div will show briefly before the user exits the print dialogue), but it's a way more simpler code to use and manage.
I think you could / should move the last focus() and print() calls to a onload handler for the iframe, to get it to happen after styles are loaded and applied.
I've just run into the same issue and did the following:
setTimeout(() => {
window.frames["iFrame"].focus();
window.frames["iFrame"].print();
}, 500)
This appears to have sorted it for me. I hate using timeout and the length is guess work at best but as it's not system critical it's something I can run with for now.
There is a single Page Application in AngularJS.
It has nested tabs. In the inner tab there is a button on which some event gets fired.I need to trigger the click event of this button present on the inner tab Button gets rendered after both the tabs are rendered. What is the best way to wait until the tabs render themselves and the button is available.
I tried using 'while loop'(i.e keep looping until id for button is undefined) and $timeout(set timeout to 2-3 seconds) service but both have their consequences when there is delay in tab render.
Please suggest if there exists a better approach.
Even though this question is really old, I've found a solution that works for me and I think it might be helpful for some people.
Calling element.getBoundingClientRect() before executing further code worked for me. According to docs this method returns information about the position relative to the viewport (docs):
The Element.getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.
Assuming that the screen has to render to find information about the position of an element, this function would technically wait for the html element to render or even make the html element render.
Remember, this is only an assumation and I can't guarantee that it works.
Things have changed quite a bit since this question was asked, so there's now a much nicer solution.
requestAnimationFrame(() => {
// this will be called just before the next video frame.
// That will be after any changes to the DOM have been completed.
});
The docs are here...
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
You can do it with jQuery:
$(document).ready(callbackFn);
or native JS:
document.addEventListener('readystatechange', function onReadyStateChange() {
if (document.readyState !== "complete")
return;
callbackFn();
}, false);
I'm trying to unbind an event from a specific element and upon research, I found this. Which is useful. In itself. I didn't know you could do that. But:
Is there a way to make it work in a browser/Chrome extension? I'm talking about content scripts.
The reason why this doesn't work the way it's described there is that the website which has attached the event in question with its own script is using a different jQuery object than the one in my extension's includes/ folder. And I can try to search the event via jQuery._data(el, 'click'); but that is my jQuery object, not the one of the website where the events are apparently stored. I'm glad I figured that out after hours of fiddling around.
Or maybe it is possible to access the website's jQuery object itself?
EDIT:
What I'm ultimately trying to achieve works in theory but … it's complicated. The original script uses a plugin event and keeps reinstalling it with .on('mouseleave',….
Anyway, this is what I got thanks to you, pdoherty926:
var $el = $('div.slideshow');
$('h2', $el).click(function(){ console.log('ouch!'); }); // test event
var $slides = $('.slides', $el).detach();
$copy = $el.clone(false);
$slides.prependTo($copy);
$el.replaceWith($copy);
The test event doesn't get triggered but the event I'm actually trying to remove still fires. I can imagine figuring it out, though, now that I got closer to my goal.
Okay, the aforementioned re-installation on mouseleave really messed up this otherwise satisfying suggestion. (The site is using the jQuery Timer plug-in by Cyntaxtech). So here's how I solved it instead: I simply changed the class name (-.-' )
Now the re-installation code cannot find the element anymore.
This is how my finished script looks like:
function stop_happening() {
var $el = $('div.fullwall div.slideshow');
$el
// first, stop the current automation.
.timer('stop') // Timer plug-in
// next, change class name in order to prevent the timer
// from being started again.
.removeClass('slideshow').addClass('slideshow-disabled-automation');
//--- copied some extra code from the website itself for the onclick
// events which are supposed to keep working. I wish I could do *that*
// programmatically but I'm glad I got as far as I got. ---//
// […]
}
After updating the DOM with a new element (e.i. body.append(html)), I cannot immediately get the height of the newly updated element (e.g. body.height()). A partial fix is setting a time interval and getting the height at the end of time interval (setTimeout("alert(body.height)", 500)).
Any better ideas? Like a callback after DOM completely updates? Thanks.
Edit:
This only happens when there is a lot going on in the background (i.e. when page first loads). It would seem like the page update is almost instantaneous if nothing else is being processed. What I need is a callback, or an indicator of when the DOM is reformed!
Edit:
The problem is from the browser being 'distracted' by other processes, which makes the browser unable to update immediately after the append of the element. How do I fix this?
The timeout method works because the rendering engine is given a chance to display the new element there by giving it a change to render it and thus assigning it a height.
You can set the timeout to 0 and still have the same effect.
With jQuery works fine for me.
In the jsfiddle demo I put the next code:
$(function(){
var jTest = $('#test');
console.log('The height:',jTest.innerHeight(),jTest.height()); //Show me 'The height: 20 20'
jTest.append('<div><br/>How are you?</div>');
console.log('The height:',jTest.innerHeight(),jTest.height()); //Show me 'The height: 60 60'
});
Unless you mean javascript only solution or put a jsFiddle demo to show your error.