I'm building an interface that uses AJAX with an HTML fallback. I'm setting up all my <a> tags to work without AJAX first, and if Javascript is enabled, each link will have an "onclick" function attached to it that sends the same exact query string to a different page on my server.
My original link will look like this:
<a class="ajax" href="http://example.com/page?key1=value1&key2=value2">Link</a>
How do I retrieve "key1=value1&key2=value2" as a string from the above href link via Javascript? I will be making AJAX requests that look like http://example.com/ajax?key1=value1&key2=value2.
You can attach a click handler either to individual links:
var links = document.getElementsByTagName('a');
var index;
for (index = 0; index < links.length; ++index) {
links.onclick = linkClickHandler;
}
function linkClickHandler() {
var x = this.href.indexOf('?');
var query = x >= 0 ? this.href.substring(x + 1) : null;
if (query) {
// Do the ajax thing...
// (your code here)
// ...and prevent the link from being followed
return false;
}
}
...or (and this is probably better) to document itself:
document.onclick = function(e) {
var target, x, query;
e = e || window.event;
target = e.target;
if (target.tagName.toUpperCase() === "A") {
x = target.indexOf('?');
query = x >= 0 ? target.substring(x + 1) : null;
if (query) {
// Do the ajax thing...
// (your code here)
// ...and prevent the link from being followed
return false;
}
}
};
In either case, on modern browsers you might want to use addEventListener rather than onclick, and call preventDefault on the event object. But IE8 still uses attachEvent rather than addEventListener.
(return false; from an old-fashioned DOM0 event handler like onclick prevents the default action of the event; details.)
tl;dr
Looking at your comments on other answers, this is what you need
linkElement.search.substr(1)
The answer...
You can access the same properties you would with window.location.
For querystring of a href it would be (document.querySelector('a#mylink')).search
Other accessible properties
.hash
.host
.hostname
.href
.origin
.pathname
.port
.protocol
.search
In your case, for all the links on a page use this little script
*I am only selecting links with actual hrefs.
[].forEach.call(document.querySelectorAll('a[href]'), function(el) {
var queryString = el.search.substr(1)
el.onclick = function(e){
e.preventDefault() // don't redirect and stuff...
// do the magic here with the queryString
}
})
This sample code should be enough to help you parse what you need. Note that I added an id to the anchor to make it easy to access.
<!DOCTYPE html>
<HTML>
<HEAD>
<SCRIPT type="text/javascript">
function parse() {
var el = document.getElementById("foo");
var href = el.href;
var pos = href.indexOf("?");
alert(href.substring(pos+1));
}
</SCRIPT>
</HEAD>
<BODY bgcolor="white" onLoad="parse()">
<a id="foo" class="ajax" href="http://example.com/page?key1=value1&key2=value2">Link</a>
</BODY>
</HTML>
Related
This is probably an easy solution but right now I can't figure out how to make it work
$(".a").click(function () {
if ($("#btnCollapse").css('display')!='none')
$("#btnCollapse").click();
});
Then I tried using vanilla js, I know I am missing something....
var anchor = document.querySelectorAll(".a");
var button = document.querySelectorAll("#btnCollapse");
function collapseNav() {
anchor.addEventListener('click', function() {
button.style.display="none"
});
button.click();
}
querySelectorAll returns a nodelist so you need to loop through its result.
For the #bntCollapse use querySelector, it returns as single element. For elements with an id, and if you need to find many, you can use getElementById, which is faster than querySelector
To get the style, use window.getComputedStyle as it will return a style being set using external CSS as well, which element.style.display won't.
var anchors = document.querySelectorAll(".a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener('click', function(e){
var btn = document.querySelector("#btnCollapse");
if (window.getComputedStyle(btn,null).getPropertyValue("display") != 'none') {
btn.click();
}
})
}
Note, you can use foreach to loop the elements, though based on how, in IE, Edge and Safari it might not work, so test it thoroughly, therefore I used a for..loop for maximum browser support.
Direct conversion of your "jQuery" code:
if (button.style.display != 'none')
button.click();
It can be done using closure-in-loop,
var anchor = document.querySelectorAll(".a");
var button = document.querySelectorAll("#btnCollapse");
Array.from(anchor).forEach(a => {
a.addEventListener('click', function() {
if(button.style.display!="none"){
button.click();
}
});
});
querySelectorAll() returns a collection of elements, not a single one, hence you need to loop over it. The button has an id so you can select it using querySelector() to get a single instance back.
You also have no collapseNav() function in the jQuery version so your event handler will be added on load.
Finally the logic is not the same. In the jQuery you only click the button if it's display is not none. Try this:
var anchor = document.querySelectorAll(".a");
var button = document.querySelector("#btnCollapse");
anchor.forEach(function(el) {
el.addEventListener('click', function() {
if (button.style.display != 'none')
button.click();
});
});
ok so I can achieve what I am looking to do using jQuery very easily using the following code:
<script type="text/javascript">
// <![CDATA[
$('.pnTrig').on('click', function(e){
e.preventDefault();
var id = $(this).attr('href').split("_").pop(); // get last character of string
console.log(id); // check correct character is returned
P7_TP3ctrl('p7TP3_1',id); // controls to show accordian panels
});
// ]]>
</script>
What I would like is for someone to show me how to convert this jQuery code to native JavaScript please.
Here is a documented vanilla Javascript version.
function clickHandler(event) {
// execute preventDefault() if don't want the link to be followed (default browser behavior)
event.preventDefault();
// get the event target (what `this` would refer to in jQuery)
var target = event.target;
// same as before
var id = target.href.split('_').pop();
// same as before
P7_TP3ctrl('P7_TP3', id);
}
// get all elements with `pnTrig` class
var triggers = document.querySelectorAll('.pnTrig');
// apply the event handler to all matching elements
for (var i = 0; i < triggers.length; i++) {
// attach the event handler (don't define the event handling function here)
triggers[i].addEventListener('click', clickHandler, false);
}
function P7_TP3ctrl(label, id) {
console.log("Clicked id: ", id);
}
Link One
Link Two
I have the following Javascript that on a single mouse click in a table cell with id="freq-table" populates consecutive <input> form fields with id="searchTerm(x)" with the cell's value. It's referenced in the <body> tag as:
<body onload="populateFields()>
and <table> tag as:
<table onclick="populateFields()>
var index=0;
function populateFields(){
var ft_id = document.getElementById("freq-table");
var alltds = ft_id.getElementsByTagName("td");
for (var i in alltds) {
alltds[i].onclick = function () {
if(index==0) {
searchTerm1.value = this.innerHTML;
} else {
setThis(this.innerHTML);
}
}
}
if (index<2) {
index++;
} else {
index = 1;
}
}
function setThis(value) {
document.getElementById("searchTerm"+index).value = value;
}
When trying to make the function more universal by passing the element id (as follows), it now takes a SECOND mouse click to start populating the fields.
<table onclick="populateFields(this)" id="freq-table">
function populateFields(element){
var alltds = element.getElementsByTagName("td");
What is it about the revision that's changing the behavior? Am I just incorrectly passing the id? Or is revised function now expecting a variable to be passed to it in <body> tag? It's confusing because: If I am incorrectly passing the id, why would the function work consecutively AFTER the first mouse click? What is the fix for this, please?
You have some heavy code here, where the first table click (or body onload) sets additional click event handlers.
What you should do instead is use event delegation. With event delegation, the click event handler is attached to the table but knows which cell was clicked (the target).
[Update] Code sample based on the above article:
var index=0;
var tableIds=["freq-table1","freq-table2","freq-table3"];
for (var i=0;i<tableIds.length;i++) {
var currentId=tableIds[i];
var table=document.getElementById(currentId);
table.onclick = function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
while(target != this) {
if (target.nodeName == 'TD') {
// target is our cell
setThis(target.innerHTML);
}
target = target.parentNode;
}
// increment index modulo 3
index=(index+1)%3;
}; // end of onclick function
} // end of for loop
Live demo: http://jsfiddle.net/srVmF/2/
I think the call can come from the TD or the TR element. So, the first time the id will be 'undefined'.
Why not call the function with the event and verify the tag name:
<table onclick="populateFields(event)" id="freq-table">
Javascript
function populateFields(e) {
var source = e.target || e.srcElement;
if (e.tagName == 'table') {
var ft_id = document.getElementById(source.id);
Instead of being populated on page load, now you have to click on the table before it populates the fields.
You could leave the page load handler:
<body onload="populateAllFields()">
For every table you add a class:
<table class="mytable">
Then, the code:
function populateAllFields()
{
[].forEach.call(document.getElementsByClassName('mytable'), populateFields);
}
Your <body onload="populateFields()> isn't passing the element you want, so the initial set that would be done when the page loads is no longer happening.
You can fix it by passing the ID instead, and give the onload handler the ID.
function populateFields(id){
var ft_id = document.getElementById(id);
var alltds = ft_id.getElementsByTagName("td");
// and so on...
}
<body onload="populateFields('freq-table')">
<table onclick="populateFields(this.id)">
This is a known issue for iScroll and it only seems to happen in iOS5 where the menu completely stops working. All my sub links in iScroll are hash anchors. Does anyone have a workaround for this?
The way I handled it was to hijack the anchor links themselves and replace them with scrollToElement calls instead.
// Hijack hash anchors and scroll to them
$('a').click ( function (e) {
var id = $(this).attr('href');
if (id.substr(0,1) == '#') {
e.preventDefault();
setTimeout( function() {
scroller.scrollToElement ( id, 0 );
}, 0);
return false;
} else {
return true;
}
});
This code should only hijack links that begin with #. It then handles the scrollToElement in a setTimeout which fixes some other intermittent bugs. It works well on my end as long as your anchors are properly named with id's. If you are using name attributes instead of id attributes, you'll need to rewrite these.
This code will copy name attributes and put them in the id attribute if it is blank. You probably won't need this, though.
$('a').each (function (i, e) {
var n = $(e).attr('name');
var id = $(e).attr('id');
if ( typeof id == 'undefined' || id === null || id === '') {
$(e).attr('id', n);
}
});
When I do this,
location.hash = "test"
the url is updated and the page homes in on the element with that id.
Is there a way to stop the page from homing in to that element?
Solution
You cannot prevent this behaviour, but you can fool it by temporarily hiding the target, eg. like that (it has nothing to do with jQuery):
// obtain "target" DOM (not jQuery) element and do this:
var original_id = target.id; // save original ID
target.id = null; // set target's ID
location.hash = 'test'; // issue location.hash change
target.id = original_id; // set ID to original value
Generalized solution
Or more general example:
// make target_hash set to a hash you want to not lead you to page section and:
var element = document.getElementById(target_hash); // find element by ID
var original_id = element.id; // save original ID
location.hash = target_hash; // previously defined by you (eg. 'test')
element.id = original_id; // reset ID
Demo / proof
The live example can be as follows, in the event handler attached through jQuery (demo here: http://jsfiddle.net/DaZfH/):
some_link.on('click', function(event){
event.preventDefault();
var target = document.getElementById('target');
var original_id = target.id;
target.id = null; // unset ID
location.hash = 'target';
target.id = original_id;
});
Disclaimer
But indeed others are right: moving you to the correct place in the document is the correct behaviour. If you are doing things like I mentioned, then your solution is pretty hakish and there is definitely a better way to do that.
Is there a way to stop the page from homing in to that element
Yes. Although the hashchange event is not cancelable, you can reset its unwanted behavior like this.
var popY,
popX;
//When location.hash is changed, popstate is the first event to fire
//Use this event to record current state
window.addEventListener('popstate', popstateHandler);
function popstateHandler() {
popY = window.scrollY;
popX = window.scrollX;
}
//Reset scroll position with recorded values when hashchange fires (after page is scrolled)
window.addEventListener('hashchange', hashchangeHandler);
function hashchangeHandler() {
window.scrollTo(popX, popY);
}
That's the basis of it. You might want to do some proofing for IE, and implement your reasons for doing this: Animate scroll, active something etc..
Polyfill for scrollY, scrollX:
if(!('scrollY' in window)) {
Object.defineProperty(window, 'scrollY', {
get: function () {
return window.pageYOffset
}
});
Object.defineProperty(window, 'scrollX', {
get: function () {
return window.pageXOffset
}
})
}