Getting the current script DOM object in a (jquery) ajax request - javascript

I have a html component that includes some javascript.
The component is a file in a template engine, so it can be used
in the initial rendering of the whole html page
as stand-alone html rendered through an ajax request
The javascript should be applied to an object in the template, i.e. :
<div class="grid" >
<div class="item" id="item_13">
This is item 13
</div>
<div class="item" id="item_14">
This is item 14
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$(HOW_DO_I_GET_PREVIOUS_ELEMENT???).someEffect(params)
})
</script>
I've checked this similar question but the best answers seem to rely on the current script being the last one in the 'scripts' variable as the next ones are not loaded yet. If I append the html and js with an ajax request, it will not be the case.
To be 100% clear : the question is about getting the previous object WITHOUT reference to any specific attribute : no unique id for the tag, no random id as there is theoretically always a chance it will show up twice, no unique class attribute,as exactly the same component could be displayed in another part of the HTML document.

Simple solution involving a two step process:
1) find out which element your script tag is
2) find the previous sibling of that element
in code:
<div id="grid">
<!-- ... -->
</div>
<script type="text/javascript">
var scripts = document.getElementsByTagName("script");
var current = scripts[scripts.length-1];
var previousElement = current.previousSibling;
// there may be whitespace text nodes that should be ignored
while(previousElement!==null && previousElement.nodeType===3) {
previousElement = previousElement.previousSibling; }
if(previousElement!==null) {
// previousElement is <div id="grid"> in this case
$(document).ready(function(){
$(previousElement).someEffect(params);
});
}
</script>
Is this good web programming? No. You should know which elements should have effects applied to them based on what you're generating. If you have a div with an id, that id is unique, and your generator can tell that if it generates that div, it will also have to generate the js that sets up the jQuery effect for it.
But let's ignore that; does it work? Like a charm.

If you can give your <script/> block an Id you could easily call prev() to get the previous element.
<script type="text/javascript" id="s2">
$(document).ready(function(){
$("#s2").prev().append("<h1>Prev Element</h2>");
})
</script>
Example on jsfiddle.

You will need to get a way to reference the script tag immediately after the "grid" div. As #Mark stated, the easiest way to do this is by giving the script tag a unique id. If this is beyond your control, but you do have control of the script contents (implicit by the fact that you are creating it) you can do something like this:
var UniqueVariableName;
var scripts = document.getElementsByTagName('script');
var thisScript = null;
for(var i = 0; i < scripts.length; i++){
var script = $(scripts[i]);
if(script.text().indexOf('UniqueVariableName') >= 0){
thisScript = script;
break;
}
}
if(thisScript){
thisScript.prev().append("<h1>Prev Element</h2>");
}
Hack? Yes. Does it Work? Also, yes.

Here's something that works in FF, Chrome and IE 8, untried anywhere else. It looks at the element before the last element on the page (which is the script being parsed), stores it locally (with a self calling function) so the load handler can use it.
http://jsfiddle.net/MtQ5R/2/
<div class="grid" >
<div class="item" id="item_13">
This is item 13
</div>
<div class="item" id="item_14">
This is item 14
</div>
</div><script>(function(){
var nodes = document.body.childNodes;
var prevSibling = nodes[nodes.length - 2];
$(document).ready(function(){
console.log( prevSibling );
})
})();</script>
Having said that. I still have to mention that you're tightly coupling the behavior (JS) and HTML, by putting them into the same file which kind of goes against the web flow of separating them. Also, I don't know how you'd expect this to work with an AJAX request since you're not just adding it to the HTML as it's being rendered. In that case, it would be very easy to get a reference to the html you just inserted though.

Related

Creating a div within an existing div in javascript issues

I have a problem, I wanted to create a div in html as a container and in javascript create new divs within the container based on a number input from a user prompt.
My html and javascript look like this.
HTML:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="stylesheet.css">
<title>Sketchpad</title>
</head>
<body>
<button type="button">Reset</button>
<div class= "container">
</div>
<script src="javascript.js"></script>
<script src="jQuery.js"></script>
</body>
JS
var row = prompt("Enter number of rows:");
var column = prompt("Enter number of columns:");
function createGrid(){
var cont = document.getElementsByClassName('container');
for(i=1; i<column; i++){
var sketchSquare = document.createElement('div');
cont.appendChild(sketchSquare);
}
}
createGrid(column);
I end up with this error: Uncaught TypeError: cont.appendChild is not a function.
I imagine this is something to do with the getElementsByClassName?
I do have a solution which involves creating the container div in javascript and appending the smaller squares inside the container div. I was just curious as to why my first soltuion didn't work?
cont[0].appendChild(myDiv) is a function.
When you document.getElements By Class Name as the name implies you are getting many elements (an array of sorts) of elements and this array don't have the same functions as each of its elements.
Like this:
var thinkers = [
{think: function(){console.log('thinking');}
];
thinkers don't have the method .think
but thinkers[0].think() will work.
try this: open your javascript console by right clicking and doing inspect element:
then type:
var blah = document.getElementsByClassName('show-votes');
blah[0].appendChild(document.createElement('div'));
It works!
also if you want to use jQuery which I do see you added...
you can do:
var cont = $('container');
cont.append('<div class="sketchSquare"></div>');
Try that out by doing this:
First get an environment that has jQuery.
Hmm maybe the jQuery docs have jQuery loaded!
They do: http://api.jquery.com/append/.
Open the console there and at the bottom where the console cursor is type:
$('.signature').append('<div style="background: pink; width: 300px; height: 300px"></div>');
You'll notice that you add pink boxes of about 300px^2 to 2 boxes each of which have the "signature" class.
By the way, prompt gives you a string so you'll have to do row = Number(row); or row = parseInt(row, 10); and another thing don't use that global i do for(var i = 0; ...
var cont = document.getElementsByClassName('container');
Because that^ doesn't return a node, it'll return an HTMLCollection.
https://www.w3.org/TR/2011/WD-html5-author-20110705/common-dom-interfaces.html#htmlcollection-0
You need to pick an individual node from that collection before appending.
There could be a couple of issues that could cause this. Without fully giving the answer here's what it could be at a high level.
Your script is ran before the DOM is fully loaded. Make sure that your script is ran after the DOM is present in the page. This can be accomplished using either the DOMReady event ($(document).ready equivalent without jQuery) or simply making sure your script tag is the last element before the closing body tag. (I usually prefer the former)
When you utilize document.getElementsByClassName('container') (https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName) this method returns an array therefore you would either need to apply the operation to all elements of the result or just select the zero-th as document.getElementsByClassName('container')[0]. As an alternative, if you would like to be more explicit you could also place an id on the container element instead to more explicitly state which element you would like to retrieve. Then, you would simply use document.getElementById([id]) (https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) and this would get back a single element not a collection.
The result of prompt is a string. Therefore you would have to first parse it as an integer with parseInt(result, 10) where 10 is simply the radix or more simply you want a number that is from 0-10.
You should include jquery library before your script, it`s important
<script src="jQuery.js"></script>
<script src="javascript.js"></script>

Need JS to modify the element following the script

I have a report authoring tool that lets me add descriptive text in the report header, prior to a table containing the data. The examples included with the tool show how include Javascript in the description for various special effects. I would like to change certain cells in the table into links to other reports. Here's the HTML produced by the reporting tool.
<div class="element-info">
<div class="description">My Description</div>
<div class="properties">
<table>...</table>
</div>
</div>
I have tried replacing "My Description" with the following, but (perhaps unsurprisingly) it's changing something other than the table.
<div>My Description
<script type="text/javascript">
// currentScript is supported in my version of Firefox.
var me = document.currentScript;
// go up two levels to get the enclosing div
var element_info = me.parentElement.parentElement;
// from there we want the properties div, then the table
var mytable = element_info.lastChild.firstChild;
mytable.style.color = "red";
</script>
</div>
I expect that the problem is that when the script runs, the HTML in the following div has not yet been parsed. Mozilla says that the defer attribute will be ignored in scripts without a src= attribute, and I've verified that it does nothing.
Although my example code is using plain Javascript, the authoring tool is based on jQuery, so it's full repertoire is available if needed.
If the problem involves the fact that the html hasn't yet been parsed, you can immediately gain a reference to the script, but only later utilize it, once the document is loaded. It would look like this:
<div>My Description
<script type="text/javascript">
// Get the reference immediately...
var script_of_interest = document.currentScript;
// And only use it once everything is loaded:
window.onload = function() {
var element_info = script_of_interest.parentElement.parentElement;
var mytable = element_info.lastChild.firstChild;
mytable.style.color = "red";
};
</script>
</div>
Make sure your the node your script operates on is loaded before the execution, otherwise your the node may be undefined or just nothing. You may try wrap your code with
$(document).ready(function(){
//your code
}).

Find the tag JavaScript is running in

Generating HTML source on backend, I am using separate independent widgets.
I am simply including pieces of markup like this to the resulting HTML output.
<div>
I want to work with this DOM element
<script>
new Obj(/*but I can't get this <div> as a parameter! */);
</script>
</div>
I'm looking for a way to find the DOM element in which the obj is created (Without any unique IDs). This would add flexibility to my app and speed up the development. But is that technicaly possible in JavaScript?
You could seed an element in there and then get it's parent, and then remove the element.
<div>
I want to work with this DOM element
<script>
document.write("<div id='UniqueGUID_3477zZ7786_' style='display:none;'></div>");
var thatDivYouWanted;
(function(){
var target = document.getElementById("UniqueGUID_3477zZ7786_");
thatDivYouWanted = target.parentNode;
target.parentNode.removeChild(target);
})();
new Obj(/*but I can't get this <div> as a parameter! */);
</script>
</div>
The following code works:
<script>
function Obj(color) {
var scriptTags = document.getElementsByTagName("script");
var scriptTag = scriptTags[scriptTags.length - 1];
// find parent or do whatsoever
var divTag = scriptTag.parentNode;
divTag.style.backgroundColor = color;
}
</script>
<div>
I want to work with this DOM element
<script>new Obj("green");</script>
</div>
<div>
I want to work with this DOM element
<script>new Obj("yellow");</script>
</div>
<div>
I want to work with this DOM element
<script>new Obj("lime");</script>
</div>
This method has very simple code and has almost zero impact on performance.
Note: I am pretty sure this won't work IE6 (as far as I remember it does not support manipulating open tags).
I believe your approach is not ideal. If you're trying to obtain the <div>, it should be done programmatically in a conventional way using JavaScript and the API's that empower you to query the target <div>
Instead of executing inline, you can execute in a separate scope in a controlled way (DOM Ready then Query then Your Method). You can target your div by using an ID, CSS class name, or any other CSS selector in JavaScript.
This allows you to pretty much do the follow anywhere you want, not inline.
// on dom ready...
var div = document.getElementById('myDiv'), // replace with any other selector method
myObject = new Object(div);
Need to find your div? https://developer.mozilla.org/en-US/docs/DOM/Document.querySelectorAll
If you know beforehand how the page will be structured, you could use for example:
document.getElementsByTagName("div")[4]
to access the 5th div.

Find the div containing the current script [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How may I reference the script tag that loaded the currently-executing script?
I am trying to make a javascript function that includes a html doc on a page via AJAX, as a way of making a PHP-esque include() with no serverside interaction. I want the script to include the file at the location on the page the function is called from. Here's my function (assuming ajax is a valid xmlhttp object):
function include(src, elem){
ajax.open('GET', src, false);
ajax.send(null);
elem.innerHTML = ajax.responseText;
}
So this would print the contents of "src.html" in the div when it is clicked:
<div onclick="include('src.html', this);"> </div>
But I want it to load when the page does. Considering there is no onload event for divs I have to include the script in the div, which is fine:
<div id=write>
<script>include('src.html', this);</script>
</div>
But then the script has no reference to the div it is called from. Sure I could put an id on the div and pass that to the function, but I don't want to. I want to be able to call this from any unidentified element. Any ideas?
You could change your div (or other element(s)) to use a data- attribute to specify what script to run:
<div data-include="src.html"></div>
And then run a script onload of the page (or in a script block just before the closing </body> tag) that finds all elements with that attribute.
var elements = document.querySelectorAll("[data-include]");
for (var i = 0; i < elements.length; i++)
include(elements[i].getAttribute("data-include"), elements[i]);
Here's a demo of the above (with a dummy include() function that just puts the required source url string in the element rather than doing Ajax, but it shows the elements are selected correctly): http://jsfiddle.net/nnnnnn/gm2LN/
For simplicity I've used querySelectorAll() to select the elements, but note that it isn't supported in IE7 and older. But obviously you can substitute whatever other element selection method you like if you want or need to support older browsers.
Here:
<div id=write>
<script>include('src.html', this);</script>
</div>
"this" points to the window object.
I think of putting an id to the script element and doing something like this:
<div id=write>
<script id='test'>include('src.html', document.getElementById('test').parentNode);</script>
</div>
Now elem in "include" function will point to the div containing the script element. In this case you are still relying on id but not on the div's side
When the page is loaded, all scripts will be executed sequencially, as soon as they are parsed. Therefore, you just need to get the last script that is apparent in the DOM to get the currently executed script:
var script = document.scripts[document.scripts.length-1];
ajax(url, function successCallback(html) {
script.insertAdjacentHTML("afterend", html);
});
(Demo to test - notice that document.scripts needs FF 9+)
However, I see no reason not to use serverside include().
nnnnnn was on the money, but I modified it ever so softly. I ended up making an include tag with a src attribute. On pageload I loop through all the "include" tags and fill them with the data from their src attribute:
function include(src, elem){
ajax.open('GET', src, false);
ajax.send(null);
elem.innerHTML = ajax.responseText;
}
window.onload = function(){
var includes = document.getElementsByTagName('include');
for(var i = 0; i <= includes.length; i++){
var elem = includes[i];
var src = elem.getAttribute('src');
include(src, elem);
}
}
Then anywhere I want to include a html file I just include my custom element:
<include src='includeme.html'> </include>
In practice this produces a bit of popup but for my application that's fine.
Thanks for the help!

Get all html between two elements

Problem:
Extract all html between two headers including the headers html. The header text is known, but not the formatting, tag name, etc. They are not within the same parent and might (well, almost for sure) have sub children within it's own children).
To clarify: headers could be inside a <h1> or <div> or any other tag. They may also be surrounded by <b>, <i>, <font> or more <div> tags. The key is: the only text within the element is the header text.
The tools I have available are: C# 3.0 utilizing a WebBrowser control, or Jquery/Js.
I've taken the Jquery route, traversing the DOM, but I've ran into the issue of children and adding them appropriately. Here is the code so far:
function getAllBetween(firstEl,lastEl) {
var collection = new Array(); // Collection of Elements
var fefound =false;
$('body').find('*').each(function(){
var curEl = $(this);
if($(curEl).text() == firstEl)
fefound=true;
if($(curEl).text() == lastEl)
return false;
// need something to add children children
// otherwise we get <table></table><tbody></tbody><tr></tr> etc
if (fefound)
collection.push(curEl);
});
var div = document.createElement("DIV");
for (var i=0,len=collection.length;i<len;i++){
$(div).append(collection[i]);
}
return($(div).html());
}
Should I be continueing down this road? With some sort of recursive function checking/handling children, or would a whole new approach be better suited?
For the sake of testing, here is some sample markup:
<body>
<div>
<div>Start</div>
<table><tbody><tr><td>Oops</td></tr></tbody></table>
</div>
<div>
<div>End</div>
</div>
</body>
Any suggestions or thoughts are greatly appreciated!
My thought is a regex, something along the lines of
.*<(?<tag>.+)>Start</\1>(?<found_data>.+)<\1>End</\1>.*
should get you everything between the Start and end div tags.
Here's an idea:
$(function() {
// Get the parent div start is in:
var $elie = $("div:contains(Start)").eq(0), htmlArr = [];
// Push HTML of that div to the HTML array
htmlArr.push($('<div>').append( $elie.clone() ).html());
// Keep moving along and adding to array until we hit END
while($elie.find("div:contains(End)").length != 1) {
$elie = $elie.next();
htmlArr.push($('<div>').append( $elie.clone() ).html());
};
// htmlArr now has the HTML
// let's see what it is:
alert(htmlArr.join(""));
});​
Try it out with this jsFiddle example
This takes the entire parent div that start is in. I'm not sure that's what you want though. The outerHTML is done by $('<div>').append( element.clone() ).html(), since outerHTML support is not cross browser yet. All the html is stored in an array, you could also just store the elements in the array.

Categories