Targeting dynamic elements in HTML widget with JavaScript - javascript

I'm trying to use Javascript to retrieve elements from an HTML widget on the page. I can't just use the usual document.getelementsbyclassname etc. as the widget is treated as an embedded section of the page.
I have tried this:
var iframe = document.getElementsByClassName("widgetclassname")[0];
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
I've used that in the past for iframes but not for this. I'm getting this error:
Uncaught TypeError: Cannot read property 'contentDocument' of undefined
I've tried adding a delay and moving around items in case it's a timing thing (trying to get things before it's loaded properly) but it made no difference.
I can't use ID instead of classname as the widget doesn't have one and I can't change that.

Cannot read property 'contentDocument' of undefined means "Hey! iframe is undefined!" Which means that your getElementsByClassName() is returning an empty node list.
We cannot help you further without a way of looking at the document you are trying to query. Are you using the developer tools to ensure that the <iframe> you want has that class on it? Exactly, no typos, no case changes?
Alternatively, you can try using:
// first iframe in the document
var iframe = document.querySelector('iframe');
// third iframe in the document
var iframe = document.querySelectorAll('iframe')[2];
Edit: in response to our chat discussion, the problem is that the page is dynamically loading content, and the script you are trying to run is taking place before that content has been loaded.
A simple hack is to wait for the element you want to load and then run the code you want. For example:
document.title = "Search Results - AvenaGo";
var waitForHotel = setInterval(function(){
var hotelName = document.querySelector('.hotel_page-hotel_name');
if (hotelName) {
clearInterval(waitForHotel);
document.title = hotelName.textContent + ' - AvenaGo';
}
}, 100);
Every 100ms this code will run and check to see if an element with the class hotel_page-hotel_name has loaded yet. If it has, it will stop checking, and modify the title of the page based on the text in that element.

Related

OneNote JavaScript API access element

I have tried to create my first One Note Add In using the JavaScript API. I have tried the example in the MS documentaion (Build your first OneNote task pane add-in). This one works.
Now I want to try to change the formatting of an element in the document. For example I want to change the font colour of a text. However, I have not yet found a way to access the elements in a document.
Can I access elements in a document via a JS Add In to change their "style" property?
How can I do that?
Thanks
Micheal
Finally, I found a way to access the OneNote page content from the JS Add In. You can load the page content using
var page = context.application.getActivePage();
var pageContents = page.contents;
context.load(pageContents);
Now you have access to the page content in the qued commands.
return context.sync().then( function() {
var outline = pageContents.items[0].outline;
outline.appendHtml("<p>new paragraph</p>");
var p = outline.paragraphs;
context.load(p);
...
});
So consequently you can access element by element in document the hirarchy.

access an element inside of an iframe inside of a jquery dialog box

I need to "load a page" inside of a page that the user is already on instead of redirecting them to that new page. It is my understanding that an iframe is the best way of doing this. I like jQuery's dialog box as a wrapper for this iframe content, as it looks nice and allows the user to reposition the popup as well as close it easily.
I'm having trouble accessing the elements on this iframe however. I want to be able to take data from the user's original window and add it to the new iframe's input elements, but I can't seem to find the syntax for doing this.
There is something in the code for the iframe page that overwrites jQuery's use of $, so I can't use $('#foo').text('bar'). I've been trying to find the correct syntax in pure javascript - like
var a = window.frames["e_frame"].document.getElementById ("title");
a.text = 'foobar';
where e_frame is the id I've given to my iframe, and title is the id of an element that loads in the iframe. I can see these both clearly in the developer console, but I cannot access them and I'm not sure what I'm doing wrong. Typing in the command above in the console gives me this error:
VM364:1 Uncaught TypeError: Cannot read property 'document' of undefined
at <anonymous>:1:28
and typing window.frames into the console to debug myself gives me way too many lines to go through since I'm not sure what I'm looking for.
Any ideas?
Have you tried this ?
var iframe = document.getElementById("e_frame");
var iframe_contents = iframe.contentDocument.getElementById('title');

Remove html wrapper in iframe in jQuery

I don't know how to remove the html wrapper in the iframe.
An example:
var content = '<html><body>A page</body></html>';
$('iframe').contents().find('html').html(content);
Even if it does not look like it in the developer tools, the above now have 2 html tags.
var html = $('iframe').contents().find('html html').height();
When using the above I get a height. When only using one html in the selector I just get 0. It proves that there are 2 html tags.
How can I get rid of the html wrapper in the iframe?
In my case I will not modify the content variable. It's fetched from elsewhere.
UPDATE
I tried this:
$('iframe').find('html').replaceWith(content);
But it gave me:
Failed to execute 'replaceChild' on 'Node': Nodes of type '#text' may not be inserted inside nodes of type '#document'.
From my experience, you should use document.write:
var content = '<html><body>A page</body></html>';
var iframeDoc = $('iframe')[0].contentDocument; // jQuery is just for the example of course
iframeDoc.write(content);
See -DEMO-
If you want to overwrite previous set content used with snippet before, you will need to call iframeDoc.open(); before calling iframeDoc.write(content);. It doesn't really makes sense but it is...

javascript multiple forms on the same rendered screen

I'm working with classic ASP.
I have an 2 includes that have 2 different forms on them. They are both unique in name. However, when I try to read the value of the one the elements in the 2nd form I get an error saying it is null. However, when I view the source in Firebug I can see that in face there is a value in that element.
My javascript code:
console.log(document.getElementById('focusValue').value);
Output from firebug:
<input id="focusValue" type="hidden" value="1006" name="focusValue">
Is there something I need to do because there are 2 forms on this "rendered" screen? The only other thing I think I should mention is that these pages are in an iFrame. Not sure if that really matters...
An iFrame creates a separate document within the containing document, you need to get a reference to that document before you can access its content. There is a reasonable tutorial at http://www.dyn-web.com/tutorials/iframes/.
If you only have one iFrame in the page, then you can reference it by:
var frame = window.frames[0];
Or you could use an id with getElementById. To get a reference to the document:
var doc;
if (frame) {
doc = frame.contentDocument || frame.contentWindow.document;
}
Now you can get to the input element:
var input = doc && doc.getElementById('focusValue');
Of course this all depends on you complying with the same origin policy, otherwise you can't access the frame content.
Can't see your page, so it's hard to debug. Assuming the script runs AFTER the forms. The only thing i can think is that there is more than one element on the page with the id "focusValue".
What happens when you console.log(document.getElementById('focusValue'))

Recommended method to locate the current script?

I am writing a script that needs to add DOM elements to the page, at the place where the script is located (widget-like approach).
What is the best way to do this?
Here are the techniques I am considering:
Include an element with an id="Locator" right above the script. Issues:
I don't like the extra markup
If I reuse the widget in the page, several elements will have the same "Locator" id. I was thinking about adding a line in the script to remove the id once used, but still...
Add an id to the script. Issues:
even though it seems to work, the id attribute is not valid for the script element
same issue as above, several elements will have the same id if I reuse the script in the page.
Use getElementsByTagName("script") and pick the last element. This has worked for me so far, it just seems a little heavy and I am not sure if it is reliable (thinking about deferred scripts)
document.write: not elegant, but seems to do the job.
[Edit] Based on the reply from idealmachine, I am thinking about one more option:
Include in the script tag an attribute, for example goal="tabify".
Use getElementsByTagName("script") to get all the scripts.
Loop through the scripts and check the goal="tabify" attribute to find my script.
Remove the goal attribute in case there's another widget in the page.
[Edit] Another idea, also inspired by the replies so far:
Use getElementsByTagName("script") to get all the scripts.
Loop through the scripts and check innerHTML to find my script.
At the end of the script, remove the script tag in case there's another widget in the page.
Out of the box : document.currentScript (not supported by IE)
I've worked for OnlyWire which provides, as their main service, a widget to put on your site.
We use the var scripts = document.getElementsByTagName("script"); var thisScript = scripts[scripts.length - 1]; trick and it seems to work pretty well. Then we use thisScript.parentNode.insertBefore(ga, thisScript); to insert whatever we want before it, in the DOM tree.
I'm not sure I understand why you consider this a "heavy" solution... it doesn't involve iteration, it's a pure cross-browser solution which integrates perfectly.
This works with multiple copies of same code on page as well as with dynamically inserted code:
<script type="text/javascript" class="to-run">
(function(self){
if (self == window) {
var script = document.querySelector('script.to-run');
script.className = '';
Function(script.innerHTML).call(script);
} else {
// Do real stuff here. self refers to current script element.
console.log(1, self);
}
})(this);
</script>
Either document.write or picking the last script element will work for synchronously loaded scripts in the majority of web pages. However, there are some options I can think of that you did not consider to allow for async loading:
Adding a div with class="Locator" before the script. HTML classes has the advantage that duplicates are not invalid. Of course, to handle the multiple widget case, you will want to change the element's class name when done adding the HTML elements so you do not add them twice. (Note that it is also possible for an element to be a member of multiple classes; it is a space-separated list.)
Checking the src of each script element can ensure that tracking code (e.g. Google Analytics legacy tracking code) and other scripts loaded at the very end of the page will not prevent your script from working properly when async loading is used. Again, to handle the multiple widget case, you may need to remove the script elements when done with them (i.e. when the desired code has been added to the page).
One final comment I will make (although you may already be aware of this) is that when coding a widget, you need to declare all your variables using var and enclose all your code within: (JSLint can help check this)
(function(){
...
})();
This has been called a "self-executing function" and will ensure that variables used in your script do not interfere with the rest of the Web page.
Whether you drop a <script> tag in or a <div class="mywidget">, you're adding something to the markup. Personally, I prefer the latter as the script itself is only added once. Too many scripts in the page body can slow down the page load time.
But if you need to add the script tag where the widget is going to be, I don't see what's wrong with using document.write() to place a div.
I just found another method that seems to answer my question:
How to access parent Iframe from javascript
Embedding the script in an iframe allows to locate it anytime, as the script always keeps a reference to its own window.
I vote this the best approach, as it'll always work no matter how many times you add the script to the page (think widget). You're welcome to comment.
What pushed me to consider iframes in the first place was an experiment I did to build a Google gadget.
In many cases this work well (hud.js is the name of the scipt):
var jsscript = document.getElementsByTagName("script");
for (var i = 0; i < jsscript.length; i++) {
var pattern = /hud.js/i;
if ( pattern.test( jsscript[i].getAttribute("src") ) )
{
var parser = document.createElement('a');
parser.href = jsscript[i].getAttribute("src");
host = parser.host;
}
}
Also you can add individual script's name inside them.
either inside some js-script
dataset['my_prefix_name'] = 'someScriptName'
or inside HTML - in the <script> tag
data-my_prefix_name='someScriptName'
and next search appropriate one by looping over document.scripts array:
... function(){
for (var i = 0, n = document.scripts.length; i < n; i++) {
var prefix = document.scripts[i].dataset['my_prefix_name']
if (prefix == 'whatYouNeed')
return prefix
}
}
I haven't had access to internet explorer since forever, but this should work pretty much everywhere:
<script src="script.js"
data-count="30"
data-headline="My headline"
onload="uniqueFunctionName(this)"
defer
></script>
and inside script.js:
window.uniqueFunctionName = function (currentScript) {
var dataset = currentScript.dataset
console.log(dataset['count'])
console.log(dataset['headline'])
}

Categories

Resources