Apply a parent to an iframe - javascript

I have fixed the video's height and width ratio using FluidVids.js. Now I want to wrap my iframe with a parent. I know that the parents working by manually throwing it inside the HTML-file. But the template I am working on needs to do this automatically.
So what I'm asking for is a (pure) JavaScript for wrapping my iframe with a div class I call fwparent. The HTML looks something like this:
<div class="post">
...
<iframe src="a-nice-video" allowFullscreen></iframe>
...
</div>
FluidVids overrides the standard sizes on the iframe. But my div class post has some paddings that I need to override with the fwparent class!
So the result should be something like this:
<div class="post">
...
<div class="fwparent"><iframe src="a-nice-video" allowFullscreen></iframe></div>
...
</div>
And remember, pure JavaScript. No extra HTTP requests for jQuery or stuff like that...
I have no experience what-so-ever with JavaScript. So be nice ;)
I have something similar which applies to images:
function imgWrap(parent) {
if (!parent || parent.nodeType !== 1) {
return false;
}
else {
var images = parent.getElementsByTagName('img'),
newDiv = document.createElement('div'),
tmp;
newDiv.className = 'parent';
for (var i = 0, len = images.length; i < len; i++) {
tmp = newDiv.cloneNode(true);
parent.insertBefore(tmp, images[i])
tmp.appendChild(images[i]);
}
}
}
imgWrap(document.querySelector('div.post'));
And jQuery does something like this:
$('iframe').wrap('<div class="fwparent" />');
But I want it to be pure JavaScript. And apply to all iframes...

Something like this?
var div = document.getElementById('wrapper').appendChild(document.createElement('div'));
div.className = 'fwparent';
div.appendChild(document.getElementById('iframe'));
EDIT
To wrap more than one iframe you need to use a loop:
var frms = document.getElementsByTagName('iframe'),
post = document.getElementById('post'),
div, n;
for (n = 0; n < frms.length; n++) {
div = post.appendChild(document.createElement('div'));
div.className = 'fwparent';
div.appendChild(frms[0]); // *
}
* = looks like we need 0 instead of n here, a live nodelist created by getElementsByTagName() is updated when appending iframes to a new location.
A live demo at jsFiddle.

Related

random div on page load

I am trying to randomize the hero content of a home page. I have this simple code, but it affects all the divs on the page, and I only want it to affect a few.
var elems = $("div");
if (elems.length) {
var keep = Math.floor(Math.random() * elems.length);
for (var i = 0; i < elems.length; ++i) {
if (i !== keep) {
$(elems[i]).hide();
}
}
}
Here is my html:
<div id="hero1">One</div>
<div id="hero2">Two</div>
<div id="hero3">Three</div>
<div id="constant">This content does not rotate.</div>
There is another caveat to this, I need it to work within a crappy CMS that strips out my class tags. So it has to be a solution that identifies the divs based on id.
How about
var elems = $('div').not('#constant')
?
The not function removes matching elements from the set it's called on.

It is possible to comment and uncomment block of code from DOM tree using JQuery? [duplicate]

This sounds a little crazy, but I'm wondering whether possible to get reference to comment element so that I can dynamically replace it other content with JavaScript.
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar place holder: some id-->
</body>
</html>
In above page, can I get reference to the comment block and replace it with some content in local storage?
I know that I can have a div place holder. Just wondering whether it applies to comment block.
Thanks.
var findComments = function(el) {
var arr = [];
for(var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if(node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
// whatever you were going to do with the comment...
console.log(commentNodes[0].nodeValue);
It seems there are legitimate (performance) concerns about using comments as placeholders - for one, there's no CSS selector that can match comment nodes, so you won't be able to query them with e.g. document.querySelectorAll(), which makes it both complex and slow to locate comment elements.
My question then was, is there another element I can place inline, that doesn't have any visible side-effects? I've seen some people using the <meta> tag, but I looked into that, and using that in <body> isn't valid markup.
So I settled on the <script> tag.
Use a custom type attribute, so it won't actually get executed as a script, and use data-attributes for any initialization data required by the script that's going to initialize your placeholders.
For example:
<script type="placeholder/foo" data-stuff="whatevs"></script>
Then simply query those tags - e.g.:
document.querySelectorAll('script[type="placeholder/foo"]')
Then replace them as needed - here's a plain DOM example.
Note that placeholder in this example isn't any defined "real" thing - you should replace that with e.g. vendor-name to make sure your type doesn't collide with anything "real".
Building off of hyperslug's answer, you can make it go faster by using a stack instead of function recursion. As shown in this jsPerf, function recursion is 42% slower on my Chrome 36 on Windows and 71% with IE11 in IE8 compatibility mode. It appears to run about 20% slower in IE11 in edge mode but faster in all other cases tested.
function getComments(context) {
var foundComments = [];
var elementPath = [context];
while (elementPath.length > 0) {
var el = elementPath.pop();
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === Node.COMMENT_NODE) {
foundComments.push(node);
} else {
elementPath.push(node);
}
}
}
return foundComments;
}
Or as done in TypeScript:
public static getComments(context: any): Comment[] {
const foundComments = [];
const elementPath = [context];
while (elementPath.length > 0) {
const el = elementPath.pop();
for (let i = 0; i < el.childNodes.length; i++) {
const node = el.childNodes[i];
if (node.nodeType === Node.COMMENT_NODE) {
foundComments.push(node);
} else {
elementPath.push(node);
}
}
}
return foundComments;
}
There is an API for document nodes traversal: Document#createNodeIterator():
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_COMMENT
);
// Replace all comment nodes with a div
while(nodeIterator.nextNode()){
var commentNode = nodeIterator.referenceNode;
var id = (commentNode.textContent.split(":")[1] || "").trim();
var div = document.createElement("div");
div.id = id;
commentNode.parentNode.replaceChild(div, commentNode);
}
#header,
#content,
#some_id{
margin: 1em 0;
padding: 0.2em;
border: 2px grey solid;
}
#header::after,
#content::after,
#some_id::after{
content: "DIV with ID=" attr(id);
}
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar placeholder: some_id -->
</body>
</html>
Edit: use a NodeIterator instead of a TreeWalker
If you use jQuery, you can do the following to get all comment nodes
comments = $('*').contents().filter(function(){ return this.nodeType===8; })
If you only want the comments nodes of the body, use
comments = $('body').find('*').contents().filter(function(){
return this.nodeType===8;
})
If you want the comment strings as an array you can then use map:
comment_strings = comments.map(function(){return this.nodeValue;})
Using document.evaluate and xPath:
function getAllComments(node) {
const xPath = "//comment()",
result = [];
let query = document.evaluate(xPath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0, length = query.snapshotLength; i < length; ++i) {
result.push(query.snapshotItem(i));
}
return result;
}
getAllComments(document.documentElement);
from my testing, using xPath is faster than treeWalker:
https://jsben.ch/Feagf
This is an old question, but here's my two cents on DOM "placeholders"
IMO a comment element is perfect for the job (valid html, not visible, and not misleading in any way).
However, traversing the dom looking for comments is not necessary if you build your code the other way around.
I would suggest using the following method:
Mark the places you want to "control" with markup of your choice (e.g a div element with a specific class)
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
Find the placeholders the usual way (querySelector/classSelector etc)
var placeholders = document.querySelectorAll('placeholder');
Replace them with comments and keep reference of those comments:
var refArray = [];
[...placeholders].forEach(function(placeholder){
var comment = document.createComment('this is a placeholder');
refArray.push( placeholder.parentNode.replaceChild(comment, placeholder) );
});
at this stage your rendered markup should look like this:
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
Now you can access each of those comments directly with your built refArray and do whatevere it is you wanna do... for example:
replace the second comment with a headline
let headline = document.createElement('h1');
headline.innerText = "I am a headline!";
refArray[1].parentNode.replaceChild(headline,refArray[1]);
If you just want to get an array of all comments from a document or part of a document, then this is the most efficient way I've found to do that in modern JavaScript.
function getComments (root) {
var treeWalker = document.createTreeWalker(
root,
NodeFilter.SHOW_COMMENT,
{
"acceptNode": function acceptNode (node) {
return NodeFilter.FILTER_ACCEPT;
}
}
);
// skip the first node which is the node specified in the `root`
var currentNode = treeWalker.nextNode();
var nodeList = [];
while (currentNode) {
nodeList.push(currentNode);
currentNode = treeWalker.nextNode();
}
return nodeList;
}
I am getting over 50,000 operations per second in Chrome 80 and the stack and recursion methods both get less than 5,000 operations per second in Chrome 80. I had tens of thousands of complex documents to process in node.js and this worked the best for me.
https://jsperf.com/getcomments/6

Access dynamic generated div id

I have some div ids that are generated dynamicly via php
<div id='a<?php echo $gid?>>
How can I access them in JavaScript? All these divs start with "A" followed by a number.
Is there some kind of search function
getElementById(a*)?
Thanks for any help
No generic JavaScript function for this (at least not something cross browser), but you can use the .getElementsByTagName and iterate the result:
var arrDivs = document.getElementsByTagName("div");
for (var i = 0; i < arrDivs.length; i++) {
var oDiv = arrDivs[i];
if (oDiv.id && oDiv.id.substr(0, 1) == "a") {
//found a matching div!
}
}
This is the most low level you can get so you won't have to worry about old browsers, new browsers or future browsers.
To wrap this into a neater function, you can have:
function GetElementsStartingWith(tagName, subString) {
var elements = document.getElementsByTagName(tagName);
var result = [];
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.id && element.id.substr(0, subString.length) == subString) {
result.push(element);
}
}
return result;
}
The usage example would be:
window.onload = function() {
var arrDivs = GetElementsStartingWith("div", "a");
for (var i = 0; i < arrDivs.length; i++) {
arrDivs[i].style.backgroundColor = "red";
}
};
Live test case.
In case you choose to use jQuery at some point (not worth for this thing alone) all the above code turns to single line:
$(document).ready(function() {
$('div[id^="a"]').css("background-color", "blue");
});
Updated fiddle, with jQuery.
No, you need a fixed id value for getElementById to work. However, there are other ways to search the DOM for elements (e.g. by CSS classes).
You can use querySelectorAll to get all divs that have an ID starting with a. Then check each one to see if it contains a number.
var aDivs = document.querySelectorAll('div[id^="a"]');
for(var index = 0, len = aDivs.length; index < len; index++){
var aDiv = aDivs[index];
if(aDiv.id.match(/a\d+/)){
// aDiv is a matching div
}
}​
DEMO: http://jsfiddle.net/NTICompass/VaTMe/2/
Well, I question myself why you would need to select/get an element, that has a random ID. I would assume, you want to do something with every div that has a random ID (like arranging or resizing them).
In that case -> give your elements a class like "myGeneratedDivs" with the random ID (if you need it for something).
And then select all with javascript
var filteredResults=document.querySelectorAll(".myGeneratedDivs").filter(function(elem){
....
return true;
});
or use jQuery/Zepto/YourWeaponOfChoice
var filteredResults=$(".myGeneratedDivs").filter(function(index){
var elem=this;
....
return true;
});
If you plan to use jQuery, you can use following jQuery selectors
div[id^="a"]
or
$('div[id^="id"]').each(function(){
// your stuff here
});
You will have to target the parent div and when someone click on child div inside a parent div then you can catch the child div.
<div id="target">
<div id="tag1" >tag1</div>
<div id="tag1" >tag2</div>
<div id="tag1" >tag3</div>
</div>
$("#target").on("click", "div", function() {
var showid = $(this).attr('id');
alert(showid)
});
getElementById() will return the exact element specified. There are many javascript frameworks including jQuery that allow much more powerful selection capabilities. eg:
Select an element by id: $("#theId")
Select a group of elements by class: $(".class")
Select subelements: $("ul a.action")
For your specific problem you could easily construct the appropriate selector.

Search for non-nested nodes with specific attribute set

The short long of it is I'm working on a small library in javascript that will replace <div src="somesite"></div> with the content from the specified source. This would allow coders to create dynamic pages without having to do more work server-side without the annoyance of using iframes.
What I need is an efficent way to get the top most div nodes of a branch with an src attribute. E.G:
<div src="somesite/pagelet.htm" id="div1">
<div src="somesite/fallback.htm" id="div2"></div>
</div>
<div src="somesite/pagelet2.htm" id="div3"></div>
I want to retrieve #div1 and #div3 and ignore #div2 until later. At the moment I'm using the following function, but am wondering if there is a more efficent way to do this:
function getRootElementsByAttribute(rootEle, tag, attr) {
try {
tag = tag.toLowerCase();
if (rootEle.tagName.toLowerCase() === tag && rootEle.hasAttribute(attr)) {
return [rooEle]
}
var eles = rootEle.getElementsByTagName(tag),
nodes = [], ele, isRoot, eleParent, a;
for (a=0; a<eles.length; a++) {
ele = eles[a];
if (ele.hasAttrinute(attr)) {
isRoot = true;
eleParent = ele;
while ((eleParent = eleParent.parentNode)) {
if (eleParent.tagName.toLowerCase() === 'div' && eleParent.hasAttribute(attr)) {
isRoot = false;
break;
}
}
if (isRoot == true) nodes.push(ele)
}
}
}catch(e){}
return nodes;
}
Please no answers suggesting the use of a library. It seems overkill to import a whole library when all it would be used for is this single function
You could try to use an XPath expression to get all root divs with the attribute source using something like the following XPath expression:
/div[#src]
/div selects all divs that are on the root level. For all divs in the document use //div.
[#src] specifies that you only want nodes with the 'src' attribute.
var xmlDoc = //load your document here
var xpath = "/div[#src]"
var nodes = xmlDoc.evaluate(xpath, xmlDoc, null, XPathResult.ANY_TYPE,null);

Is it possible to get reference to comment element/block by JavaScript?

This sounds a little crazy, but I'm wondering whether possible to get reference to comment element so that I can dynamically replace it other content with JavaScript.
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar place holder: some id-->
</body>
</html>
In above page, can I get reference to the comment block and replace it with some content in local storage?
I know that I can have a div place holder. Just wondering whether it applies to comment block.
Thanks.
var findComments = function(el) {
var arr = [];
for(var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if(node.nodeType === 8) {
arr.push(node);
} else {
arr.push.apply(arr, findComments(node));
}
}
return arr;
};
var commentNodes = findComments(document);
// whatever you were going to do with the comment...
console.log(commentNodes[0].nodeValue);
It seems there are legitimate (performance) concerns about using comments as placeholders - for one, there's no CSS selector that can match comment nodes, so you won't be able to query them with e.g. document.querySelectorAll(), which makes it both complex and slow to locate comment elements.
My question then was, is there another element I can place inline, that doesn't have any visible side-effects? I've seen some people using the <meta> tag, but I looked into that, and using that in <body> isn't valid markup.
So I settled on the <script> tag.
Use a custom type attribute, so it won't actually get executed as a script, and use data-attributes for any initialization data required by the script that's going to initialize your placeholders.
For example:
<script type="placeholder/foo" data-stuff="whatevs"></script>
Then simply query those tags - e.g.:
document.querySelectorAll('script[type="placeholder/foo"]')
Then replace them as needed - here's a plain DOM example.
Note that placeholder in this example isn't any defined "real" thing - you should replace that with e.g. vendor-name to make sure your type doesn't collide with anything "real".
Building off of hyperslug's answer, you can make it go faster by using a stack instead of function recursion. As shown in this jsPerf, function recursion is 42% slower on my Chrome 36 on Windows and 71% with IE11 in IE8 compatibility mode. It appears to run about 20% slower in IE11 in edge mode but faster in all other cases tested.
function getComments(context) {
var foundComments = [];
var elementPath = [context];
while (elementPath.length > 0) {
var el = elementPath.pop();
for (var i = 0; i < el.childNodes.length; i++) {
var node = el.childNodes[i];
if (node.nodeType === Node.COMMENT_NODE) {
foundComments.push(node);
} else {
elementPath.push(node);
}
}
}
return foundComments;
}
Or as done in TypeScript:
public static getComments(context: any): Comment[] {
const foundComments = [];
const elementPath = [context];
while (elementPath.length > 0) {
const el = elementPath.pop();
for (let i = 0; i < el.childNodes.length; i++) {
const node = el.childNodes[i];
if (node.nodeType === Node.COMMENT_NODE) {
foundComments.push(node);
} else {
elementPath.push(node);
}
}
}
return foundComments;
}
There is an API for document nodes traversal: Document#createNodeIterator():
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_COMMENT
);
// Replace all comment nodes with a div
while(nodeIterator.nextNode()){
var commentNode = nodeIterator.referenceNode;
var id = (commentNode.textContent.split(":")[1] || "").trim();
var div = document.createElement("div");
div.id = id;
commentNode.parentNode.replaceChild(div, commentNode);
}
#header,
#content,
#some_id{
margin: 1em 0;
padding: 0.2em;
border: 2px grey solid;
}
#header::after,
#content::after,
#some_id::after{
content: "DIV with ID=" attr(id);
}
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar placeholder: some_id -->
</body>
</html>
Edit: use a NodeIterator instead of a TreeWalker
If you use jQuery, you can do the following to get all comment nodes
comments = $('*').contents().filter(function(){ return this.nodeType===8; })
If you only want the comments nodes of the body, use
comments = $('body').find('*').contents().filter(function(){
return this.nodeType===8;
})
If you want the comment strings as an array you can then use map:
comment_strings = comments.map(function(){return this.nodeValue;})
Using document.evaluate and xPath:
function getAllComments(node) {
const xPath = "//comment()",
result = [];
let query = document.evaluate(xPath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0, length = query.snapshotLength; i < length; ++i) {
result.push(query.snapshotItem(i));
}
return result;
}
getAllComments(document.documentElement);
from my testing, using xPath is faster than treeWalker:
https://jsben.ch/Feagf
This is an old question, but here's my two cents on DOM "placeholders"
IMO a comment element is perfect for the job (valid html, not visible, and not misleading in any way).
However, traversing the dom looking for comments is not necessary if you build your code the other way around.
I would suggest using the following method:
Mark the places you want to "control" with markup of your choice (e.g a div element with a specific class)
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
Find the placeholders the usual way (querySelector/classSelector etc)
var placeholders = document.querySelectorAll('placeholder');
Replace them with comments and keep reference of those comments:
var refArray = [];
[...placeholders].forEach(function(placeholder){
var comment = document.createComment('this is a placeholder');
refArray.push( placeholder.parentNode.replaceChild(comment, placeholder) );
});
at this stage your rendered markup should look like this:
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
Now you can access each of those comments directly with your built refArray and do whatevere it is you wanna do... for example:
replace the second comment with a headline
let headline = document.createElement('h1');
headline.innerText = "I am a headline!";
refArray[1].parentNode.replaceChild(headline,refArray[1]);
If you just want to get an array of all comments from a document or part of a document, then this is the most efficient way I've found to do that in modern JavaScript.
function getComments (root) {
var treeWalker = document.createTreeWalker(
root,
NodeFilter.SHOW_COMMENT,
{
"acceptNode": function acceptNode (node) {
return NodeFilter.FILTER_ACCEPT;
}
}
);
// skip the first node which is the node specified in the `root`
var currentNode = treeWalker.nextNode();
var nodeList = [];
while (currentNode) {
nodeList.push(currentNode);
currentNode = treeWalker.nextNode();
}
return nodeList;
}
I am getting over 50,000 operations per second in Chrome 80 and the stack and recursion methods both get less than 5,000 operations per second in Chrome 80. I had tens of thousands of complex documents to process in node.js and this worked the best for me.
https://jsperf.com/getcomments/6

Categories