Can I target an element before it's closed? - javascript

I'm wanting to add a class to the body tag without waiting for the DOM to load, but I'm wanting to know if the following approach would be valid. I'm more concerned with validity than whether the browsers support it for now.
<body>
$("body").addClass("active");
...
</body>
Thanks,
Steve

The .elementReady() plugin seems to be pretty close to what you're looking for.
It operates by using a setInterval loop, that exits as soon as document.getElementById() returns an element for a given id.
You could probably do a slight modification of that plugin (or commit an update/patch) to allow for generic selectors (at least for "tagNames") instead of just ids.
I don't believe there is any truly reliable cross-browser compatible way to address an element before it's loaded - other than this sort of setInterval hacking
Unless you are able to place your javascript command inside the target element like #JasonBunting suggests.

If the element doesn't exist in the DOM, the search will fail to find it and the action won't be applied. If you can't do it in the $(document).ready() function, you might want to try putting the code after the element being referenced. I believe this will work.
<body>
<div id='topStories'></div>
<script type='text/javascript'>
$('div#topStories').addClass('active');
</script>
</body>
If you need to add the class to the body, I would definitely use $(document).ready().

Short answer: it depends. Apparently, according to my tests, the answer seems to be yes, depending on what you want. I just tested this:
<html>
<head>
<style type="text/css">
.foobar { background-color: #CCC; }
</style>
</head>
<body>
<script type="text/javascript">
window.document.body.className = "foobar";
</script>
<div style="border: solid 1px"><br /></div>
<script type="text/javascript">
// happens before DOM is fully loaded:
alert(window.document.body.className);
</script>
<span>Appears after the alert() call.</span>
</body>
</html>
In IE 7, when the alert() takes place, the value is set correctly, but the style hasn't yet been applied (it is quickly applied as soon as the DOM is finished loading).
In Firefox, the style has been applied by the time the alert() takes place.
Anyway, hope this is helpful to you.

Basically, the answer is no. In IE6 and Firefox 2 (the browsers I have the most experience in), the element isn't in the DOM until after the close tag (or the page is done rendering, for invalid XHTML). I know that jQuery provides a convenience methods that seems to react quickly enough to avoid "flicker" in most cases. You would use it like so:
<script>
$(document).ready(function() {
$("body").addClass("active");
});
</script>
<body>
..
..
..
</body>
But that's about it for javascript.
Of course, in the example you provided, you could easily just accomplish the same effect with:
<body class="active">
</body>

That was VERY helpful.
To put a little real world to the question.
I build with the assumption that JavaScript isn't supported and then override with the JavaScript. The problem is, that when I have to wait for the DOM to load before my overrides kick in the site goes through the flicker stage as it's built. I'm hoping that if I can add a class of "active" to the body element before the rest of the site's loaded I'll be able to apply JavaScript assumed styles before the page renders.
What I don't want to do is to add this and then get a call when Firefox4 comes out that I shouldn't have done it.
If you take a look at a site I built, you'll see that it degrades gracefully, but that ficker bugs me (especially if an ad hangs the site up). I could take the other guys approach and just build it with JS assumed, but come on - that's just lazy...

Rather than adding a class to your <body> tag you might find it easier to add a class to the <html> tag by doing:
<script type="text/javascript">
document.documentElement.className = 'active';
</script>

Related

Is it ok to add elements to documentElement (the <html> element)? Will anything unexpected happen?

Are there any issues with code like the following?
document.documentElement.insertAdjacentHTML('beforeend', `
<div style="border: 5px solid pink">This content is outside the <body>, but both Firefox and Chrome render it fine.</div>
`)
Is this reliable? Or can there be issues in some browsers? From what I can tell, it is totally fine in Firefox and Chrome.
I can't find any information about this, but it seems to work.
EDIT:
The reason I found for using document.documentElement, is we can rely on the fact that document.documentElement seems to be always defined no matter when a <script>'s code runs.
However, document.body is sometimes null depending on when a <script>'s code runs.
For example, consider the following code using document.body. If you stick it in a .html file, then open it in your browser, you will see the message null.
<script>
alert(document.body)
</script>
Here's a live example: https://plnkr.co/edit/PkhHjSHi6esfrl32
Now consider this code:
<script>
alert(document.documentElement)
</script>
The message that you will see is [object HTMLHtmlElement] (or similar). Live example: https://plnkr.co/edit/nSxtGZzN8tU0hCbq
This means, that to write code using document.body, it takes more effort and requires more code.
To get the document.body version to work, we have to do something like the following:
<body>
<script>
alert(document.body)
</script>
</body>
Live example: https://plnkr.co/edit/7BcZDZX4jemhwfcV. Of course, it's not that much more code, but when following the principle of simplicity, it seems that document.documentElement leads to slightly simpler code.
In my tests so far, it seems to work just fine (anything outside the of the eventually-created <body> renders as a sibling to the body element.
What problems may placing DOM elements in document.documentElement cause, that I have not forseen?
by default scripts run synchronously just like normal code. In normal code this won't work
const b = a + 1; // ERROR! a is not defined
const a = 1;
Because a does not exist when b is trying to use it you'll get an error. HTML and scripts are exactly the same
<script>
document.getElementById('a').textContent = 'yo!'; // ERROR <div id="a"> is not defined
</script>
<div id="a"></div>
fails for exactly the same reason as the JavaScript example. <div id="a"> does not exist when the script runs
This explains your <body> issue. If you put the script before the <body> tag then it does not yet exist. If you put it after then it does.
So the simple answer is you can do whatever you want as long as the things you want to reference exist when you try to reference them.
As for adding to the root element, yes you can do that. You don't have to have a body tag. In fact Google even suggests leaving it out.
note: I said above scripts run synchronously by default. There are plenty of ways to make them run later. The modern way is with the defer keyword though that way requires the script to be in a separate file. An old and no longer recommended way is to use the load event using one of <body onload="someFunc()"> or window.onload = someFunc() or window.addEventListener('load', someFunc). In these cases the script actually runs immediately but then effectively sets a callback to be called when the rest of the page has finished loading. Most modern JavaScript no longer uses the load event and either puts the script after the stuff it needs to reference or uses defer. Also es6 modules always "defer"
note that <html> and <body> are special tags. The HTML element is always the root element whether you declare one or not. Also, both <html> and <body> will only have one element each regardless of how many you declare them. Further, <body>, if you declare it, will always be a child of the root element.

Sort iframes without reloading in Angular [duplicate]

Take a look at this simple HTML:
<div id="wrap1">
<iframe id="iframe1"></iframe>
</div>
<div id="warp2">
<iframe id="iframe2"></iframe>
</div>
Let's say I wanted to move the wraps so that the #wrap2 would be before the #wrap1. The iframes are polluted by JavaScript. I am aware of jQuery's .insertAfter() and .insertBefore(). However, when I use those, the iFrame loses all of its HTML, and JavaScript variables and events.
Lets say the following was the iFrame's HTML:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
// The variable below would change on click
// This represents changes on variables after the code is loaded
// These changes should remain after the iFrame is moved
variableThatChanges = false;
$(function(){
$("body").click(function(){
variableThatChanges = true;
});
});
</script>
</head>
<body>
<div id='anything'>Illustrative Example</div>
</body>
</html>
In the above code, the variable variableThatChanges would...change if the user clicked on the body. This variable, and the click event, should remain after the iFrame is moved (along with any other variables/events that have been started)
My question is the following: with JavaScript (with or without jQuery), how can I move the wrap nodes in the DOM (and their iframe childs) so that the iFrame's window stays the same, and the iFrame's events/variables/etc stay the same?
It isn't possible to move an iframe from one place in the dom to another without it reloading.
Here is an example to show that even using native JavaScript the iFrames still reload:
http://jsfiddle.net/pZ23B/
var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
This answer is related to the bounty by #djechlin
A lot of search on the w3/dom specs and didn't find anything final that specifically says that iframe should be reloaded while moving in the DOM tree, however I did find lots of references and comments in the webkit's trac/bugzilla/microsoft regarding different behavior changes over the years.
I hope someone will find anything specific regarding this issue, but for now here are my findings:
According to Ryosuke Niwa - "That's the expected behavior".
There was a "magic iframe" (webkit, 2010), but it was removed in 2012.
According to MS - "iframe resources are freed when removed from the DOM". When you appendChild(node) of existing node - that node is first removed from the dom.
Interesting thing here - IE<=8 didn't reload the iframe - this behavior is (somewhat) new (since IE>=9).
According to Hallvord R. M. Steen comment, this is a quote from the iframe specs
When an iframe element is inserted into a document that has a browsing context, the user agent must create a new browsing context, set the element's nested browsing context to the newly-created browsing context, and then process the iframe attributes for the "first time".
This is the most close thing I found in the specs, however it's still require some interpretation (since when we move the iframe element in the DOM we don't really do a full remove, even if the browsers uses the node.removeChild method).
Whenever an iframe is appended and has a src attribute applied it fires a load action similarly to when creating an Image tag via JS. So when you remove and then append them they are completely new entities and they refresh. Its kind of how window.location = window.location will reload a page.
The only way I know to reposition iframes is via CSS. Here is an example I put together showing one way to handle this with flex-box:
https://jsfiddle.net/3g73sz3k/15/
The basic idea is to create a flex-box wrapper and then define an specific order for the iframes using the order attribute on each iframe wrapper.
<style>
.container{
display: flex;
flex-direction: column;
}
</style>
<div class="container">
<div id="wrap1" style="order: 0" class="iframe-wrapper">
<iframe id="iframe1" src="https://google.com"></iframe>
</div>
<div id="warp2" style="order: 1" class="iframe-wrapper">
<iframe id="iframe2" src="https://bing.com"></iframe>
</div>
</div>
As you can see in the JS fiddle these order styles are inline to simplify the flip button so rotate the iframes.
I sourced the solution from this StackOverflow question: Swap DIV position with CSS only
Hope that helps.
If you have created the iFrame on the page and simply need to move it's position later try this approach:
Append the iFrame to the body and use a high z-index and top,left,width,height to put the iFrame where you want.
Even CSS zoom works on the body without reloading which is awesome!
I maintain two states for my "widget" and it is either injected in place in the DOM or to the body using this method.
This is useful when other content or libraries will squish or squash your iFrame.
BOOM!
Unfortunately, the parentNode property of an HTML DOM element is read-only. You can adjust the positions of the iframes, of course, but you can't change their location in the DOM and preserve their states.
See this jsfiddle I created that provides a good test bed. http://jsfiddle.net/RpHTj/1/
Click on the box to toggle the value. Click on the "move" to run the javascript.
This question is pretty old... but I did find a way to move an iframe without it reloading. CSS only. I have multiple iframes with camera streams, I dont like when they reload when i swap them. So i used a combination of float, position:absolute, and some dummy blocks to move them around without reloading them and having the desired layout on demand (resizing and all).
If you are using the iframe to access pages you control, you could create some javascript to allow your parent to communicate with the iframe via postMessage
From there, you could build login inside the iframe to record state changes, and before moving dom, request that as a json object.
Once moved, the iframe will reload, you can pass the state data into the iframe and the iframe listening can parse the data back into the previous state.
PaulSCoder has the right solution. Never manipulate the DOM for this purpose. The classic approach for this is to have a relative position and "flip" the positions in the click event. It's only not wise to put the click event on the body, because it bubbles from other elements too.
$("body").click(function () {
var frame1Height = $(frame1).outerHeight(true);
var frame2Height = $(frame2).outerHeight(true);
var pos = $(frame1).css("top");
if (pos === "0px") {
$(frame1).css("top", frame2Height);
$(frame2).css("top", -frame1Height);
} else {
$(frame1).css("top", 0);
$(frame2).css("top", 0);
}
});
If you only have content that is not cross-domain you could save and restore the HTML:
var htmlContent = $(frame).contents().find("html").children();
// do something
$(frame).contents().find("html").html(htmlContent);
The advantage of the first method is, that the frame keeps on doing what it was doing. With the second method, the frame gets reloaded and starts it's code again.
At least in some circumstances a shadow dom with slotting might be an option.
<template>
<style>div {outline:1px solid black; height:45px}</style>
<div><slot name="a" /></div>
<div><slot name="b" /></div>
</template>
<div id="shadowhost">
<iframe src="data:text/html,<button onclick='this.innerText+=`!`'>!</button>"
slot="a" height=40px ></iframe>
</div>
<button onclick="ifr.slot= (ifr.slot=='a') ? 'b' : 'a';">swap</button>
<script>
document.querySelector('#shadowhost').attachShadow({mode: 'open'}).appendChild(
document.querySelector('template').content
);
ifr=document.querySelector('iframe');
</script>
In response to the bounty #djechlin placed on this question, I have forked the jsfiddle posted by #matt-h and have come to the conclusion that this is still not possible.
http://jsfiddle.net/gr3wo9u6/
//this does not work, the frames reload when appended back to the DOM
function swapFrames() {
var w1 = document.getElementById('wrap1');
var w2 = document.getElementById('wrap2');
var f1 = w1.querySelector('iframe');
var f2 = w2.querySelector('iframe');
w1.removeChild(f1);
w2.removeChild(f2);
w1.appendChild(f2);
w2.appendChild(f1);
//f1.parentNode = w2;
//f2.parentNode = w1;
//alert(f1.parentNode.id);
}

How do I change the title of a web page using javascript?

I know this is a duplicate question, but I've tried a few approaches and I'm not able to get the solution I need.
I need to change the title of a web page, and I thought Javascript would be a good candidate. I've read many disapproving comments already, talking about how changing the title will negatively affect SEO-I'm not worried about that just now.
I'm able to change the title by reassigning a value in an inline script:
<input type="button" value="Click me." onclick="document.title = 'Some new title';" />
But using an inline script in this case is out of the question. I tried loading an embedded script tag above & below the body of the script, no go. This is what I settled on, and it didn't work initially (keep reading):
<script>
document.addEventListener("load", function changeTitle(){
document.title = "FUBAR";
}, true);
</script>
I've tried adding/removing the 'true' value at the end of the parameter list and that doesn't change anything. I avoided naming the function, then named it, and that didn't change anything. What DID work was changing "load" to "click". I need the title to change right after the document is finished loading...is there something else I can use, like "ready", or "onload"? Using "load" yielded no results, but I swear I've seen it used before.
Thanks!
Try using
window.addEventListener rather than document.addEventListener
See
https://developer.mozilla.org/en-US/docs/Web/Events/load
Note: More reliable is to add event listener on
"window.addEventListener".
No need to wait for the load event. Just set the title:
<html>
<head></head>
<body>
<script>document.title = "foobar"</script>
<!-- rest of document -->

How do I set a height with javascript on load

How can I set height and visibility (detailsVisible) on load? Thanks in advance.
From here:
To here:
The easiest, and best way to do this is to use a library called jQuery. It allows you to write less code, and provides a lot of pre-written functionality for you.
Firstly, include jQuery in the <head> or at the bottom of your <body> of your HTML file:
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
Next, you'll want to use this JavaScript. Make sure this is below the <script> tag for jQuery, or it won't work:
$(document).ready(function()
{
$theDiv = $('#theid'); // #theid should be the `id` attribute of the div you are trying to modify
$theDiv.css({'height': '57px'}); // the height can be set to whatever you want
$theDiv.attr('detailsVisible', true);
});
You can also run that particular code which is inside $(document).ready(); whenever you want, such as when you click on a button etc. This particular code above simply runs as soon as the page is ready.

Manipulating <body> with jQuery? (Or figuring a better solution)

I'm trying to create a JS-script to make modifications to add a footer to HTML -documents on the fly. The idea is to append a div-element at the end of the document to contain the footer, and to provide a floating fixed footer, I also need to have all of the other content wrapped in a div, basically I need something like this:
<html>
<head>
<title>Foobar</title>
</head>
<body>
<div id="contentWrapper">
<!-- Content is here -->
</div>
<div id="footerWrapper">
<!-- Footer goes here -->
</div>
</body>
</html>
The problem is, that the HTML is generated from a system where the end user's have had a little too much control over the structure (it's a blogging platform), and there's no guarantee of a certain sturcture hence I need to wrap the content in a div to ensure the footer works ok.
What I tried, and realized that doesn't work is:
$(document.body).wrap($('<div/>').attr('id','footerWrapper'));
The problem with this is that due to the fact that the HTML structure is generated by the user, I have been forced to inject links to the JS-file inside the <body>-tag. So now when I call wrap(), it seems that everything is first removed from $(document.body) and then appended in the new div. Since the JS-files are linked from inside , calling wrap() seems to remove them momentarily, and it seems that the scripts are unloaded by the browser and everything stops working and I'm left with a blank page. Not exactly what I had in mind.
Next idea was to first copy the JS-tags to the head-element to preserve them, so I wrapped them in a div (yeah, ugly, I know), and tried to copy them to the :
$(document.head).append($('#copyToHead').html());
That didn't do anything, and seems that $(document.head) isn't usable with functions such as .html() and .append().
So, now I'm out of ideas. Anyone have any ideas?
$(document.head) isn't usable with functions such as .html() and .append().
That would be because document.head is undefined
Use $("head")[0]
not clear on what your are trying to add to the head part. if you are simply trying to add a div to the end here is a solution:
$(document).ready(function(){
$(document.body).append($('<div></div>').attr('id','mydiv').html('This is footer'));
});
idea
If leave fact, that $(document.body) doesn't exist, wrapping everything into div and then setting id through attr might be problematic (don't ask me why—it just happens). So I played with it and created this little snippet (with preview, 100% working).
Since you can't play with html, but can "append" script I did whole document manipulation through inline script.
code
<script type="text/javascript">
$(document).ready(function(){
$("body")
.wrapInner('<div id="wrapper"/>')
.append('<div id="footer">footer text</div>');
});
</script>
preview
http://jsbin.com/ezoqo4/3
edits:
further simplification and proper markup generation
I believe this should serve you better:
$('body')
.children ().wrapAll ($('<div/>').attr('id','contentWrapper'))
.end ()
.append ($('<div/>').attr('id','footerWrapper'))
;
Ref: wrapAll

Categories

Resources