I'm pretty new to using jQuery but am working on a Safari Extension at the moment and have run into a bit of a puzzle with appending some text to a page.
Basically, I am creating an extension that will append a specific string of text with another on the page within an element
<td id="name">foo</td>
Using the following jQuery code
jQuery("#name").filter(function()
{return jQuery(this).text() == 'foo'; }).append('bar');
This would result in
<td id="name">foobar</td>
This works great in a standard html page, however - the site I wish it to run on uses a different bit of JS to dynamically load the content within the element, so when testing it on the site, the element is listed as
<td id="name"></td>
and then the JS library adds the name to it, meaning I cannot append the text (the name appears in the browser window but not within the page's source). Is there a way I can get around this?
I've tried using
$(window).load()
Though this had no effect.
It just sounds like you need to delay your script so it will run after the other script that is writing "foo" runs. Generally jquery scripts are run on document load with
$(function(){ /* script */ });
so if that is too early you may need to bind to some event that is occuring when "foo" is written that you might have to hook onto your other script for. Its not clear from the question is this is an ajax thing that writes "foo" or just another script that doesn't have any additional load. You might want to post that.
If you still can't figure it out, you can always use a setTimeout() call to delay your script, but that is sloppy and not recommended.
Related
I'm creating a chrome extension that needs to hook into another script that already exists on my target web page. For simplicity's sake, I'm trying to find the following existing script element on a page and add a console.log() to it.
<script type="text/javascript">
var viewModel = new ScenePlayViewModel('', 'Ace', false);
viewModel
.load('jgWJJ2qsxx')
.then(function () {
sceneDOM = new SceneEditDOM2(viewModel.scene());
sceneDOM.init();
viewModel.isSubmitViaShareUrl(false);
viewModel.isSubmitViaUnityPackage(false);
console.log("HOOK INJECTED"); <--------------------------------------------- line to add
});
</script>
I've tried a number of solutions but none of them have seemed to work. For example, I've tried using a content script to find the script and replace the text, but it appears to run the pre-change script instead of my modified code.
// replaces javascript on website, but doesn't run new version
var scriptLoadScene = $("script:contains('new ScenePlayViewModel')"); // find the script
scriptLoadScene.text("console.log('Hello World')"); // change the text
What should I do? Basically, I'm just trying to change/add scripts to the web page to add more features.
This doesn't exactly answer your question, but hopefully will help you find a solution.
First - hopefully someone with more knowledge than me will confirm or discredit this - from my understanding, the script code is only run once, on page load, unless otherwise triggered by some event. Since Chrome extensions are triggered after the page has loaded, this script will have already been run, and anything inserted after won't run unless triggered.
I suppose you could always call the function again after you've edited it, but I don't have the knowledge or experience to predict what would happen then.
In my experience, I've just added my own '' tags with the code I wanted to run by writing them into the DOM, either into the '' or '' element.
Best of luck.
-brent
I am trying to read this HTML in the console in Chrome:
<span id="lblSummaryFreight">Kr 79</span>
Using this JS:
document.getElementById('lblSummaryFreight').innerHTML;
However, after the page has loaded, running this line returns null or invalid. If i inspect the element and then run the code, it works as intended and returns "Kr 79". So, is it some kind of DOM issue I am not aware of, or a browser-specific issue?
I am using this as a variable in Google Tag Manager, and it works in 50% of the cases. I don't have access to the source code of the webpage itself, so that's why I need to lean on this rather clunky way of getting the data.
A lot of posts on this suggest that this is because the script is fired before the DOM is ready, but I don't think this is an issue, as I am also testing this in the console after the page has loaded (and the HTML elements are present), and in Google Tag Manager I have specified that the tag should fire after DOM is ready.
Any clues?
Edit / Clarification: I can't alter the code of the page itself, only read the output source, which i am trying with JS via GTM
Don't seem to be able to resolve this issue, and as it seems to be the fault of some quirky source, I will try to figure out another way to get the data I need.
What I have learned:
The issue seems to be specific to Chrome, as it works in other browsers. This is supported by the fact that the GTM tag where this code is implemented returns correct values in ~50% of the cases
Testing the page in Android native browser, it will also return 'undefined'. After reloading the page that fires the tag, it returns correct value.
The whole DOM seems unavailable in the console. Tried also this:
document.getElementsByClassName('complete').length
Which returns 0, but there are around 10 instances of the class in the source. After inspecting anywhere on the page, it returns correct number.
Any delay in running the script won't help, only the symbolic inspecting of elements or reloading the page helps.
So my conclusion is that the way this webpage is built somehow goes against the grain of some browsers - it seems like the source goes out of the memory after the source is loaded. But this is way beyond my level of understanding.
Thanks all for all inputs!
It looks like an async issue. Try this:
//Using Jquery
$( document ).ready(function() {
document.getElementById('lblSummaryFreight').innerHTML;
});
or
//Add this at the end of the body, after all of your content
<script>
(function() {
document.getElementById('lblSummaryFreight').innerHTML;
})();
</script>
Try to change how the tag is fired in GTM. Fire them when Window Loaded, and try again.
Maybe the element is being changed in another js file.
Trying jQuery is a good point. I have had similar problems and jQuery have solved them all.
I have a script that uses document.write that needs to work within ajax, but I am having trouble finding a solution to make it work. here is the script that is loaded into the page via ajax.
<script type="text/javascript">
example_widget_id = "example-1234";
example_widget_name = "registration";
example_widget_type = "example";
document.write(unescape("%3Cscript src='https://widgets.example.com/javascripts/example_widget.js' type='text/javascript'%3E%3C/script%3E"));
</script>
What happens is that the page goes blank...which is normal for document.write and ajax. I am trying to find a way to add it via innerHTML (or another solution) but I have had no luck. I looked at this thread JavaScript Document.Write Replaces All Body Content When Using AJAX
but I can't seem to figure out how to make that work with what I have.
Thanks for any help you can provide.
UPDATE:
The script snippet is third party widget. It is used by inserting it into a frontend web page editor that allows the user to position the content on the page anywhere they want (via ajax). Once it is positioned it can also be styled with the frontend editor. When the page is how the users wants it, they can save the layout and the front end editor is disable (turned off) until needed. When this happens the script (document.write) will then load and work fine on the page as it should without the interference of the ajax based frontend editor.
I was thinking if there was a way to cache the html results of the document.write (html portion) and then that cached version could be loaded via the frontend editor. Then I can swap out the html cached version of the widget with the original script w/document.write once the front end editor becomes disabled or turn off. I played around with the logic and I am able to swap out what loads depending on the state of the front end editor. I guess my question is can a document.write content be cached or saved? I think I can handle the logic in how it is used after that.
There is no solution. document.write() replaces the document after the onload event have fired. It cannot be done.
You cannot make it to work*.
*note: Technically, you can make it to work if you write your own web browser because you then would be able to make your browser NOT behave like all other browsers and append instead of replace on document.write(). But you cannot make it to work with Chrome or IE or Firefox or Opera.
For a quick solution, use .innerHTML instead. For a better solution, learn the DOM manipulation API (or use a DOM library).
I am looking for a way to modify some text inside the HTML before it is being parsed by the browser.
More precisely, I would like to remove some tags from the HTML so the image resources would not be fetched by the browser, only when I am ready I could insert these tag back to have them loaded.
Is it possible to do that via some JS/Jquery or CSS, if so, how?
the motivation here is to be able to block the loading of some resources on a page and have them loaded only when needed according to some logic. this needs to be done by some kind of scripting added to the page
Because you're doing this in JavaScript the HTML is already being processed when it comes to launch your <script> tags.
You could move your <script> tags into the <head> from the <body>, or move it to the very beginning of the body. However the problem here is that you'll have to wait for your elements to actually be created in the DOM before you can work with them.
You could use something like setTimeout() or similar and continually look for them until you find them, but there's still going to be a slight delay between them being created and your script finding them, at which point they might already start to load.
The only surefire way is to process the markup server side long before it gets to the browser.
My answer here possibly could be of use, if you can place noscript tags in key places in your markup prior to parsing/evaluation:
Client-Side Dynamic Removal of <script> Tags in <head>
This method—for javascript-enabled agents—would delay the rendering of the entire page however, or at least the regions that you needed to affect.
basic generalised theory
Wrapper your body or specific region with a noscript tag identified with either a class or id. Place some javascript to execute directly after the close noscript that grabs the tag and reads the html contents as a string. At this point you could modify the html string however you like and then re-inject it back into the DOM replacing the noscript tag.
more specific implementation
If you know before-hand which resources you need to postpone—say all your images—you could wrap each image in-question with a noscript tag. Then trigger off some JavaScript that grabs all noscripts and rewrites the contained image html to use a placeholder or lower quality version of the image. At the same time you could set up event listeners or timeouts that inject the actual images when the time is right.
The Lazy Load Plugin for jQuery is maybe what you are looking for. It delays loading of images in long web pages.
You can use any jQuery event such as click or mouseover. You can also use your own custom events such as foobar. Default is to wait until user scrolls down and image appears on the window.
Beside all the It is also possible to delay loading of images. Following code waits for page to finish loading (not only HTML but also any visible images). Five seconds after page is finished images are loaded automatically.
$(function() {
$("img:below-the-fold").lazyload({
event : "sporty"
});
});
$(window).bind("load", function() {
var timeout = setTimeout(function() {
$("img.lazy").trigger("sporty");
}, 5000);
});
Check the delayed loading demo.
So I'm using jquery along with some plugins I wrote.
I do all the initialization in a $(document).ready(function(){}) block however this is executed when the whole DOM has been loaded and is ready to be used.
However that would take long eg. when there is a server load. Or maybe the user clicks a button that has been loaded while the rest of the page hasn't loaded yet (and thus document.ready() hasn't been executed yet) in which case it would do nothing.
So, what if I want a code to be executed right after the related part of the page has been loaded instead of waiting for the WHOLE page to be loaded?
I know placing inline code right after the html that this js operates on would do the trick but what if that code uses a library like jQuery that hasn't been loaded yet?
I know placing inline code right after the html that this js operates on would do the trick but what if that code uses a library like jQuery that hasn't been loaded yet?
That would be the only way. The HTML is parsed from top to bottom. So you can expect every script you included to be accesible after you included it.
Your page should still work without JavaScript anyway, so a user clicking a button extremely fast will just temporarily have a somewhat degraded experience.
That being said, the DOM is basically ready when the HTML document all scripts are loaded. Since you cannot execute meaningful JavaScript before the JavaScript code is loaded (duh), I'd have a close look at page performance. Sending an HTML document and 2,3 JavaScript files should not be slow.
You could also use the old-style inline event handlers, like <button onclick="registerButtonClickEvent()">. However, this would introduce a complex, potentially buggy and hardly testable layer of temporary event holding.
If your <script src="jquery-whatever.js> line precedes the first clickable element in your HTML, it is guaranteed that the jquery library will be loaded and run before the user has anything useful to click on.
Just don't add async or defer attributes to the script element.
The onload event isn't triggered from all html elements, so you're forced to wait for window load. It doesn't matter where you load jQuery since it will have to wait for document to be ready. That total time required to load jQuery plus the rest of the document will be thet same.