HTML ignores ajax call [duplicate] - java

What are the possible reasons for document.getElementById, $("#id") or any other DOM method / jQuery selector not finding the elements?
Example problems include:
jQuery silently failing to bind an event handler
jQuery "getter" methods (.val(), .html(), .text()) returning undefined
A standard DOM method returning null resulting in any of several errors:
Uncaught TypeError: Cannot set property '...' of null
Uncaught TypeError: Cannot set properties of null (setting '...')
Uncaught TypeError: Cannot read property '...' of null
Uncaught TypeError: Cannot read properties of null (reading '...')
The most common forms are:
Uncaught TypeError: Cannot set property 'onclick' of null
Uncaught TypeError: Cannot read property 'addEventListener' of null
Uncaught TypeError: Cannot read property 'style' of null

The element you were trying to find wasn’t in the DOM when your script ran.
The position of your DOM-reliant script can have a profound effect on its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (generally) executed as they're encountered. This means that order matters. Typically, scripts can't find elements that appear later in the markup because those elements have yet to be added to the DOM.
Consider the following markup; script #1 fails to find the <div> while script #2 succeeds:
<script>
console.log("script #1:", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
console.log("script #2:", document.getElementById("test")); // <div id="test" ...
</script>
So, what should you do? You've got a few options:
Option 1: Move your script
Given what we've seen in the example above, an intuitive solution might be to simply move your script down the markup, past the elements you'd like to access. In fact, for a long time, placing scripts at the bottom of the page was considered a best practice for a variety of reasons. Organized in this fashion, the rest of the document would be parsed before executing your script:
<body>
<button id="test">click me</button>
<script>
document.getElementById("test").addEventListener("click", function() {
console.log("clicked:", this);
});
</script>
</body><!-- closing body tag -->
While this makes sense and is a solid option for legacy browsers, it's limited and there are more flexible, modern approaches available.
Option 2: The defer attribute
While we did say that scripts are, "(generally) executed as they're encountered," modern browsers allow you to specify a different behavior. If you're linking an external script, you can make use of the defer attribute.
[defer, a Boolean attribute,] is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.
This means that you can place a script tagged with defer anywhere, even the <head>, and it should have access to the fully realized DOM.
<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>
Just keep in mind...
defer can only be used for external scripts, i.e.: those having a src attribute.
be aware of browser support, i.e.: buggy implementation in IE < 10
Option 3: Modules
Depending upon your requirements, you may be able to utilize JavaScript modules. Among other important distinctions from standard scripts (noted here), modules are deferred automatically and are not limited to external sources.
Set your script's type to module, e.g.:
<script type="module">
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked: ", this);
});
</script>
<button id="test">click me</button>
Option 4: Defer with event handling
Add a listener to an event that fires after your document has been parsed.
DOMContentLoaded event
DOMContentLoaded fires after the DOM has been completely constructed from the initial parse, without waiting for things like stylesheets or images to load.
<script>
document.addEventListener("DOMContentLoaded", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
Window: load event
The load event fires after DOMContentLoaded and additional resources like stylesheets and images have been loaded. For that reason, it fires later than desired for our purposes. Still, if you're considering older browsers like IE8, the support is nearly universal. Granted, you may want a polyfill for addEventListener().
<script>
window.addEventListener("load", function(e){
document.getElementById("test").addEventListener("click", function(e) {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
jQuery's ready()
DOMContentLoaded and window:load each have their caveats. jQuery's ready() delivers a hybrid solution, using DOMContentLoaded when possible, failing over to window:load when necessary, and firing its callback immediately if the DOM is already complete.
You can pass your ready handler directly to jQuery as $(handler), e.g.:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<script>
$(function() {
$("#test").click(function() {
console.log("clicked:", this);
});
});
</script>
<button id="test">click me</button>
Option 5: Event Delegation
Delegate the event handling to an ancestor of the target element.
When an element raises an event (provided that it's a bubbling event and nothing stops its propagation), each parent in that element's ancestry, all the way up to window, receives the event as well. That allows us to attach a handler to an existing element and sample events as they bubble up from its descendants... even from descendants added after the handler was attached. All we have to do is check the event to see whether it was raised by the desired element and, if so, run our code.
Typically, this pattern is reserved for elements that don't exist at load time or to avoid attaching a large number of duplicate handlers. For efficiency, select the nearest reliable ancestor of the target element rather than attaching it to the document.
Native JavaScript
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
document.getElementById("ancestor").addEventListener("click", function(e) {
if (e.target.id === "descendant") {
console.log("clicked:", e.target);
}
});
</script>
<button id="descendant">click me</button>
</div>
jQuery's on()
jQuery makes this functionality available through on(). Given an event name, a selector for the desired descendant, and an event handler, it will resolve your delegated event handling and manage your this context:
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>
<div id="ancestor"><!-- nearest ancestor available to our script -->
<script>
$("#ancestor").on("click", "#descendant", function(e) {
console.log("clicked:", this);
});
</script>
<button id="descendant">click me</button>
</div>

Short and simple: Because the elements you are looking for do not exist in the document (yet).
For the remainder of this answer I will use getElementById for examples, but the same applies to getElementsByTagName, querySelector, and any other DOM method that selects elements.
Possible Reasons
There are three reasons why an element might not exist:
An element with the passed ID really does not exist in the document. You should double check that the ID you pass to getElementById really matches an ID of an existing element in the (generated) HTML and that you have not misspelled the ID (IDs are case-sensitive!).
If you're using getElementById, be sure you're only giving the ID of the element (e.g., document.getElemntById("the-id")). If you're using a method that accepts a CSS selector (like querySelector), be sure you're including the # before the ID to indicate you're looking for an ID (e.g., document.querySelector("#the-id")). You must not use the # with getElementById, and must use it with querySelector and similar. Also note that if the ID has characters in it that aren't valid in CSS identifiers (such as a .; id attributes containing . characters are poor practice, but valid), you have to escape those when using querySelector (document.querySelector("#the\\.id"))) but not when using getElementById (document.getElementById("the.id")).
The element does not exist at the moment you call getElementById.
The element isn't in the document you're querying even though you can see it on the page, because it's in an iframe (which is its own document). Elements in iframes aren't searched when you search the document that contains them.
If the problem is reason 3 (it's in an iframe or similar), you need to look through the document in the iframe, not the parent document, perhaps by getting the iframe element and using its contentDocument property to access its document (same-origin only). The rest of this answer addresses the first two reasons.
The second reason — it's not there yet — is quite common. Browsers parse and process the HTML from top to bottom. That means that any call to a DOM element which occurs before that DOM element appears in the HTML, will fail.
Consider the following example:
<script>
var element = document.getElementById('my_element');
</script>
<div id="my_element"></div>
The div appears after the script. At the moment the script is executed, the element does not exist yet and getElementById will return null.
jQuery
The same applies to all selectors with jQuery. jQuery won't find elements if you misspelled your selector or you are trying to select them before they actually exist.
An added twist is when jQuery is not found because you have loaded the script without protocol and are running from file system:
<script src="//somecdn.somewhere.com/jquery.min.js"></script>
this syntax is used to allow the script to load via HTTPS on a page with protocol https:// and to load the HTTP version on a page with protocol http://
It has the unfortunate side effect of attempting and failing to load file://somecdn.somewhere.com...
Solutions
Before you make a call to getElementById (or any DOM method for that matter), make sure the elements you want to access exist, i.e. the DOM is loaded.
This can be ensured by simply putting your JavaScript after the corresponding DOM element
<div id="my_element"></div>
<script>
var element = document.getElementById('my_element');
</script>
in which case you can also put the code just before the closing body tag (</body>) (all DOM elements will be available at the time the script is executed).
Other solutions include listening to the load [MDN] or DOMContentLoaded [MDN] events. In these cases it does not matter where in the document you place the JavaScript code, you just have to remember to put all DOM processing code in the event handlers.
Example:
window.onload = function() {
// process DOM elements here
};
// or
// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
// process DOM elements here
});
Please see the articles at quirksmode.org for more information regarding event handling and browser differences.
jQuery
First make sure that jQuery is loaded properly. Use the browser's developer tools to find out whether the jQuery file was found and correct the URL if it wasn't (e.g. add the http: or https: scheme at the beginning, adjust the path, etc.)
Listening to the load/DOMContentLoaded events is exactly what jQuery is doing with .ready() [docs]. All your jQuery code that affects DOM element should be inside that event handler.
In fact, the jQuery tutorial explicitly states:
As almost everything we do when using jQuery reads or manipulates the document object model (DOM), we need to make sure that we start adding events etc. as soon as the DOM is ready.
To do this, we register a ready event for the document.
$(document).ready(function() {
// do stuff when DOM is ready
});
Alternatively you can also use the shorthand syntax:
$(function() {
// do stuff when DOM is ready
});
Both are equivalent.

Reasons why id based selectors don't work
The element/DOM with id specified doesn't exist yet.
The element exists, but it is not registered in DOM [in case of HTML nodes appended dynamically from Ajax responses].
More than one element with the same id is present which is causing a conflict.
Solutions
Try to access the element after its declaration or alternatively use stuff like $(document).ready();
For elements coming from Ajax responses, use the .bind() method of jQuery. Older versions of jQuery had .live() for the same.
Use tools [for example, webdeveloper plugin for browsers] to find duplicate ids and remove them.

If the element you are trying to access is inside an iframe and you try to access it outside the context of the iframe this will also cause it to fail.
If you want to get an element in an iframe you can find out how here.

As #FelixKling pointed out, the most likely scenario is that the nodes you are looking for do not exist (yet).
However, modern development practices can often manipulate document elements outside of the document tree either with DocumentFragments or simply detaching/reattaching current elements directly. Such techniques may be used as part of JavaScript templating or to avoid excessive repaint/reflow operations while the elements in question are being heavily altered.
Similarly, the new "Shadow DOM" functionality being rolled out across modern browsers allows elements to be part of the document, but not query-able by document.getElementById and all of its sibling methods (querySelector, etc.). This is done to encapsulate functionality and specifically hide it.
Again, though, it is most likely that the element you are looking for simply is not (yet) in the document, and you should do as Felix suggests. However, you should also be aware that that is increasingly not the only reason that an element might be unfindable (either temporarily or permanently).

If script execution order is not the issue, another possible cause of the problem is that the element is not being selected properly:
getElementById requires the passed string to be the ID verbatim, and nothing else. If you prefix the passed string with a #, and the ID does not start with a #, nothing will be selected:
<div id="foo"></div>
// Error, selected element will be null:
document.getElementById('#foo')
// Fix:
document.getElementById('foo')
Similarly, for getElementsByClassName, don't prefix the passed string with a .:
<div class="bar"></div>
// Error, selected element will be undefined:
document.getElementsByClassName('.bar')[0]
// Fix:
document.getElementsByClassName('bar')[0]
With querySelector, querySelectorAll, and jQuery, to match an element with a particular class name, put a . directly before the class. Similarly, to match an element with a particular ID, put a # directly before the ID:
<div class="baz"></div>
// Error, selected element will be null:
document.querySelector('baz')
$('baz')
// Fix:
document.querySelector('.baz')
$('.baz')
The rules here are, in most cases, identical to those for CSS selectors, and can be seen in detail here.
To match an element which has two or more attributes (like two class names, or a class name and a data- attribute), put the selectors for each attribute next to each other in the selector string, without a space separating them (because a space indicates the descendant selector). For example, to select:
<div class="foo bar"></div>
use the query string .foo.bar. To select
<div class="foo" data-bar="someData"></div>
use the query string .foo[data-bar="someData"]. To select the <span> below:
<div class="parent">
<span data-username="bob"></span>
</div>
use div.parent > span[data-username="bob"].
Capitalization and spelling does matter for all of the above. If the capitalization is different, or the spelling is different, the element will not be selected:
<div class="result"></div>
// Error, selected element will be null:
document.querySelector('.results')
$('.Result')
// Fix:
document.querySelector('.result')
$('.result')
You also need to make sure the methods have the proper capitalization and spelling. Use one of:
$(selector)
document.querySelector
document.querySelectorAll
document.getElementsByClassName
document.getElementsByTagName
document.getElementById
Any other spelling or capitalization will not work. For example, document.getElementByClassName will throw an error.
Make sure you pass a string to these selector methods. If you pass something that isn't a string to querySelector, getElementById, etc, it almost certainly won't work.
If the HTML attributes on elements you want to select are surrounded by quotes, they must be plain straight quotes (either single or double); curly quotes like ‘ or ” will not work if you're trying to select by ID, class, or attribute.

Related

How do I inject another JSP page into a <div> when clicking a link?

I have two different divisions in a JSP page. One contains a menu of links, when clicked the div2 (id-content) loads different pages accordingly. I am doing something like -
<div id="menu">
<ul class="navbar">
<li><a name="login" href="Login.jsp" onclick="changeContent()">Login</a>
</li></div>
and in the script I have something as -
<script language="JavaScript">
function changeContent() {
document.getElementById('content').load('Login.jsp');
}
</script>
I also tried -
document.getElementById('content').innerHTML=
"<jsp:include page="Login.jsp">";
None of the ways worked. Please suggest how should I
Try jquery..
function changeContent() {
$('#content').load('Login.jsp');
}
The solution is to use Ajax, which will asynchronously retrieve your page content that can be pasted in with the innerHTML method. See my answer to a similar question of how an Ajax call works and some introductory links.
As to why your examples in your answer don't work, in the first case there is no load() method on an Element object (unless you've defined one yourself and not shown it). In the second example, as one of the question comments says, there is probably something causing a syntax error in the javascript.
As an FYI, when there is a syntax error in some javascript in a web page, the current expression being parsed and the rest of the <script></script> block will be ignored. Since this is inside a function declaration, that function will never get defined.
For instance, an embedded quote in the included page will end the string for the innerHTML assignment. Then the javascript parser will try to parse the remainder of the HTML causing a syntax error as the HTML will not be valid javascript.
We use jquery. Add a click event handler to the anchor elements. In the click handler call $('#content').load(your_url);. You might want to use the load(url, function() { ...}) version. More info here http://api.jquery.com/load/
Your initial page comes down from the server. It's displayed by the browser. When you click on a link (or a button) in the browser, you want to fill the second div with new HTML. This is is a perfect job for an AJAX request. What the AJAX object in the browser does, is to send a POST (or whatever) string to the server. And then the Ajax object receives the HTML response back from the server. And then you can display that response data which the AJAX object contains, anywhere you want.

can ajax call other ajax page?

I have index page,which contain div section.i want to call different pages into this div section on the basis of her-link click,this is done. when calling pages that may also using ajax call,in this case ajax call not work for this page.please tell me it is possible to call ajax inside an ajax.if yes then please help me.
Update Edit:
I have created a gist where i have include all used pages,script https://gist.github.com/2786811.page service_status called the view.jsp using ajax call every 5 second,on hand view.jsp interact with database and prepare view.when we do ajax call from index.jsp, service_status called but it doesn't called view.jsp file.
Thanks
You can make ajax call on page load using $(document).ready(function () {...}); in target page you are calling.
If you are trying to dump a <script> tag with innerHTML, it won't work.
However, you could (as an example) return a JSON object with two properties:
{
html: "All your <b>HTML</b> here",
js: "alert('JavaScript to be run');"
}
Then, use the html property to put content on the page, and then eval the js property.
This isn't the best solution, since it uses the evil eval, but unless you want to rewrite your whole basic structure to use callbacks properly then it's probably your best bet.

How to configure jsoup whitelist to allow internal anchor

How do I configure a jsoup Whitelist to allow internal anchor references, without allowing any arbitrary value?
Example html:
Jump To Section 1
<!-- ... -->
<a name="section1">Section 1</a>
If I attempt to clean the code with the relaxed Whitelist the href is removed.
Jsoup.clean(html, Whitelist.relaxed().addAttributes("a", "name", "target");
returns the following:
<a target="_self">Jump To Section 1</a>
<!-- ... -->
<a name="section1">Section 1</a>
If I manually build a Whitelist and add the tags and attributes that I want, but don't call addProtocols(....) I can get jsoup to leave the href in place, but that doesn't seem like a good solution as it doesn't filter out href's that contain JavaScript. For example, I want the a tag (or at least the href) removed from the following:
Jump To Section 1
<a name="section1">Section 1</a>
Is this possible with jsoup?
I did see the following patch submission to jsoup, but it doesn't look like it made it into the jsoup code base: https://github.com/jhy/jsoup/pull/77
Whitelist whitelist=new Whitelist();
Cleaner cleaner = new Cleaner(whitelist);
whitelist.addAttributes("a","accesskey","dir","lang","style","tabindex","title","href");
cleaner.clean(doc);
If no protocols are provided/whitelisted, then all of them are implicitly allowed (see isSafeAttribute). If you want to allow internal anchors, then you need to never call addProtocol on your whitelist's anchor tags, unfortunately (well, on the href at least). It looks like there was a pull request to add support, but it was never merged.
Be aware that if you are allowing all protocols, that a malicious user can run Javascript on link click:
Some text
so be cautious of that if you do not trust your HTML.
If you want to only allow say, http, https, and anchor tags, then I believe you are out of luck.
The reply get 3 upvotes doesn't answer the question at all.
The github link mentioned in the OP is currently merged, and for others who are looking for the answer
Whitelist.relaxed().addProtocols("a", "href", "#")
Reference: Jsoup API Document

Pass Java Variables to Javascript Without Using Inline JS

Is this possible? I want to be able to pass java bean data into javascript objects, but I'd really prefer not to muck up my jsp pages with a bunch of inline script tags. I like to keep my javascript separate in external files, but how do you accomplish something like this without using inline js?
<script type="text/javascript">
var variableFromServer = '${someBean.someProperty}';
</script>
You can create a JSON file with all the data and either include it inline or fetch the JSON through Ajax - that way you don't clutter the markup with data. See http://json-taglib.sourceforge.net/ for an example of a JSP-JSON template.
I'm not sure if this is worth it but one alternative will be to set the desired value in an input filed with type="hidden" and get it's in js. But this will also pass this parameter in GET and POST request from the form.
You can either do what you're doing in the snippet (do you consider that "inline JS"?), create a div of JSON with data in it (exposed as a single string) and process it, pass JS files through the JSP process (or use a different templating system for dynamic JS pages), etc.
I'm not a huge fan of processing JS files through JSP; I'll often create an object containing all the info my JS needs in a <script> tag at the bottom of the body before including my real JS. It's kind of lazy, but it's straight-forward.
One option that I've used in the past is to configure the servlet container to run the JSP interpreter on *.js files. How to set this up will depend upon what server you are running.
Note that if you want to access any request attributes this way you will need to set them up as part of the request that fetches the JavaScript file(s) (i.e. you will have to have a servlet in front of your JavaScript...or as an alternative you can use an include directive to bring in the scripts instead of a <script src='...'> tag). Session attributes you can access without needing to have a custom servlet in front of your JavaScript files.
I'm a fan of doing it the simple and easy way, so I'd create a single script element that has a minimal number of JS variables set from Java - ideally a single JS variable that is set to an object with different properties for all the different bits of data you need to pass through. Your Java code basically just outputs JSON that will be interpreted as an object literal in the JS. Immediately after that include any external scripts - because they're included afterwards they can use the variables already created.
You can put the above in the head or at the end of the body. (Or in the middle, but that doesn't really make sense.)
<html>
<head>
</head>
<body>
<!-- actual HTML markup here -->
<script>
var variableFromServer = '${someBean.someProperty}',
objectFromServer = /* jsp to spit out JSON here as appropriate */ ;
</script>
<!-- external files included after the above will be able to access
those variables -->
<script src="external1.js" type="text/javascript"></script>
<script src="external2.js" type="text/javascript"></script>
<script src="etc.js" type="text/javascript"></script>
</body>
</html>
You certainly don't need "a bunch of inline script tags" - even if it doesn't make sense to put all the values in a single object at least create all the variables in a single script element, and then all of your other JS can be in an external file.
(Add namespacing as required.)

Call a controller method within Play! template

How can I call a controller method from within a Play! template?
I have a default controller, Application, and the hasliked() method inside that controller.
The method returns whether the logged in user has liked the post ID.
It returns "none" if the user has liked the post, otherwise it returns "block" (for the CSS)
I have added the following route: GET /hasliked/{id} Application.hasliked
I tried the following:
#{list items:postList, as:'post'}
%{
display = Application.hasliked(post.id);
%}
<div style="display: ${display}">...</div>
#{/list}
But I get this error:
Template execution error
Execution error occured in template /app/views/Application/dashboard.html. Exception raised was NullPointerException : Cannot invoke method hasliked() on null object.
Try using a fully qualified name like:
controllers.Application.hasliked()
EDIT on comment:
The issue with your exception is that you are accessing the controller to get a value. That's wrong.
Controllers in Play are used to navigate. They are static, they return "void", and they do a call to another controller method or to a render method. What you try to do may have unexpected results.
What you want to do is to get the value inside the controller and pass it as a parameter:
//On controller
public static void yourRequest() {
//...
Object display = getDisplay(); //get your value
render(display);
}
//On template
<div style="display: ${display}">...</div>
That's the recommended way.
The exception you get is (most likely) caused because your Application.hasLiked() ends up with a redirect call (either render() or call to another controller's method) and that's happening while you render the page corresponding to the initial call. So it breaks.
It would probably be a better way to do fill the information that is required into the list of items instead of calling back the controller:
Your template doesn't need to know about your controllers. It should just convert data to HTML and not acquire data from somewhere else. That's the task of the controller.
It would also be more efficient in terms of database access to fetch the like status for all items at once instead of doing several calls.
When doing refactoring (e.g. renaming methods etc of your controller) the IDE cannot help you if you call controllers from the template (unless it's aware of how Play! templates work).
If you really must do this (and again, you shouldn't) you need to fully qualify the name of the controller:
controllers.Application.hasLiked()
just like Pere Villega pointed out.
An alternative to this may be to issue an AJAX call to set the style, rather than using the controller.
Set the style of your "liked" element to display: none by default, and when your view has rendered, issue an GET request to /hasliked/ with the ID as a parameter and update your CSS styles accordingly: when the user has not already liked this, output false (or whatever you want), so that you can use JavaScript to re-define the style.
The easiest way to do this would be to use jQuery to issue a request to your controller when the view has loaded. Have a look at the Play! documentation on AJAX for some inspiration. Note that you don't have to use #{jsAction /} at all - personally I find it easier to define the jQuery calls myself.
Try the following:
<script type="text/javascript" language="javascript">
var url = "#{Application.hasliked}" + "/" + yourId;
window.location.replace(url);
</script>

Categories

Resources