omniture drupal s.prop showing previous track data - javascript

jQuery('selector1').click(function() {
s.prop3 = 'loremmmmm';
s.events = 'event11';
s.tl();
});
jQuery('selector2').click(function() {
s.prop14 = 'lorem isam';
s.events = 'event32';
s.tl();
});
`trying to track multiple prop and events when tracking one prop say s.prop2 = "";
and s.prop3 = "".
in this case getting value of s.prop3 also while tracking s.prop4 value it is not emptying the previous prop values any suggestion thanks in advance

Overall, your code should be changed to the following, but before you change it, please read my notes below, which explains the changes and the implications of them, versus what you have now.
jQuery('selector1').click(function() {
s.prop3 = 'loremmmmm';
s.events = 'event11';
s.linkTrackVars='prop3,events';
s.linkTrackEvents='event11';
s.tl(true,'o','selector1 clicks');
});
jQuery('selector2').click(function() {
s.prop14 = 'lorem isam';
s.events = 'event32';
s.linkTrackVars='prop14,events';
s.linkTrackEvents='event32';
s.tl(true,'o','selector2 clicks');
});
Firstly, a definition of Adobe Analytics (AA) "trigger" methods.
s.t() - This is meant for "page view" tracking, what you normally use to trigger AA calls when a page is first loaded. Data collected will count as a page view in reports. AA variables that have values when this gets called will be included in the http request.
s.tl() - This is meant for click (interaction) tracking, what you normally use to track link clicks or other interactions after a page is loaded. This will not count as a page view in your reports. Only variables and events that are set and registered in linkTrackVars and linkTrackEvents will be included in the http request. Note: other variables that are set are still there and in the cache; they just won't be included in the http request. So, think of linkTrackVars and linkTrackEvents as whitelists for the s.tl call.
Variable caching
AA "caches" variables that are explicitly set (e.g. s.prop1='foo';). Those variables continue to exist with their values for any subsequent s.t() or s.tl() call you make on the same page (it does not carry over from page to page via cookies).
Your current code
When you call s.tl() with no arguments passed, AA treats it as if s.t() were called, so any AA variables or events (assuming you don't overwrite them) already set will be included in the http request, even if they aren't "registered" in linkTrackVars and linkTrackEvents. This is the immediate reason why your variables are carried over. However, I want to also point out the fact that your code is also effectively counting these click interactions as page views, which is probably not what you intended.
What the new code does
The new code I have shown is under the assumption that you don't actually want these clicks to count as page views. So, I have added linkTrackVars and linkTrackEvents to "register" the events and variables.
Also notice how I added some arguments to s.tl. The first argument is traditionally a reference to the link that was clicked (e.g. in the click callback, where this is a reference to the link that was clicked, you would pass this as the first argument to s.tl. However, not all interactions on a site are actual links, and s.tl only works if the first argument is either a reference to an actual link object (more accurately, something with an href attribute), or boolean true. Also, the reason for passing it was for a legacy ClickMap feature that's always been buggy and is no longer supported by Adobe anyways. So, I always just pass true.
The second argument specifies what type of link or interaction it is. There are 3 available values: "d" (signifying download is initiated),"e" (specifying an exit from the site), and "o" ("other" - a general "catch-all" bucket). I don't know the context of these event handlers you have, so I just used "o". Feel free to use one of the other values if you feel they are more appropriate.
The 3rd argument is a string value to describe the link/interaction; a "label". Generally you should use something short but descriptive of the event that occurred, but honestly, most people don't really look at the native link reports in AA interface because they are basically useless as far as breaking it down or associating it with downstream activities. Which is why most people pop custom events, props, and eVars, and look at those reports, instead. So, more than likely you can just put some static, generic "click/interaction" type value (you must pop 3rd arg with something) and call it a day.
If you did indeed intend to count these as page views
Remove linkTrackVars and linkTrackEvents lines.
Remove the s.tl(..) calls and replace with s.t() (no arguments).
This is where it gets tricky - you must explicitly wipe any AA variables you do not wish to be part of the hit. You can set them to an empty string or delete them.
On that 3rd point, as you have probably guessed, that's a pain point. There are some easier workarounds for this, but I don't know the full context of your implementation to know if they are good options for you (or even available options).
For example, AA does have a s.clearVars() method but it is only available in (relatively) recent versions of the AppMeasurement library. So if you are still on the legacy H code library, or on one of the earlier versions of AppMeasurement, then this method won't be available. If it is available in your library version, then just call that first (no arguments). Then set your variables and s.t() call.
If s.clearVars() is not available to you, you can of course just define your own method. Essentially, s.clearVars() just loops through and deletes or sets empty string to all propN and eVarN variables, as well as most of the named AA variables (pageName, channel, events, etc.). Same thing as above: first call it to wipe the vars, then set the new ones and then trigger.
Depending on what version of AA code you use, it is possible to pass AA vars as an object payload (e.g. {prop1:'foo',events:'event'} as an argument to s.t() and s.tl() and they will only count for that http request and afterwards be wiped. But there are a number of things quirks/caveats to consider if you want to go this route, which is a whole other TL;DR. I suggest you read the online AA documentation about the s.t and s.tl methods for details.

Related

How to prevent script injection attacks

Intro
This topic has been the bane of many questions and answers on StackOverflow -and in many other tech-forums; however, most of them are specific to exact conditions and even worse: "over-all" security in script-injection prevention via dev-tools-console, or dev-tools-elements or even address-bar is said to be "impossible" to protect. This question is to address these issues and serve as current and historical reference as technology improves -or new/better methods are discovered to address browser security issues -specifically related to script-injection attacks.
Concerns
There are many ways to either extract -or manipulate information "on the fly"; specifically, it's very easy to intercept information gathered from input -to be transmitted to the server - regardless of SSL/TLS.
intercept example
Have a look here
Regardless of how "crude" it is, one can easily use the principle to fabricate a template to just copy+paste into an eval() in the browser console to do all kinds of nasty things such as:
console.log() intercepted information in transit via XHR
manipulate POST-data, changing user-references such as UUIDs
feed the target-server alternative GET (& post) request information to either relay (or gain) info by inspecting the JS-code, cookies and headers
This kind of attack "seems" trivial to the untrained eye, but when highly dynamic interfaces are in concern, then this quickly becomes a nightmare -waiting to be exploited.
We all know "you can't trust the front-end" and the server should be responsible for security; however - what about the privacy/security of our beloved visitors? Many people create "some quick app" in JavaScript and either do not know (or care) about the back-end security.
Securing the front-end as well as the back-end would prove formidable against an average attacker, and also lighten the server-load (in many cases).
Efforts
Both Google and Facebook have implemented some ways of mitigating these issues, and they work; so it is NOT "impossible", however, they are very specific to their respective platforms and to implement requires the use of entire frameworks plus a lot of work -only to cover the basics.
Regardless of how "ugly" some of these protection mechanisms may appear; the goal is to help (mitigate/prevent) security issues to some degree, making it difficult for an attacker. As everybody knows by now: "you cannot keep a hacker out, you can only discourage their efforts".
Tools & Requirements
The goal is to have a simple set of tools (functions):
these MUST be in plain (vanilla) javascript
together they should NOT exceed a few lines of code (at most 200)
they have to be immutable, preventing "re-capture" by an attacker
these MUST NOT clash with any (popular) JS frameworks, such as React, Angular, etc
does NOT have to be "pretty", but readable at least, "one-liners" welcome
cross-browser compatible, at least to a good percentile
Runtime Reflection / Introspection
This is a way to address some of these concerns, and I don't claim it's "the best" way (at all), it's an attempt.
If one could intercept some "exploitable" functions and methods and see if "the call" (per call) was made from the server that spawned it, or not, then this could prove useful as then we can see if the call came "from thin air" (dev-tools).
If this approach is to be taken, then first we need a function that grabs the call-stack and discard that which is not FUBU (for us by us). If the result of this function is empty, hazaa! - we did not make the call and we can proceed accordingly.
a word or two
In order to make this as short & simple as possible, the following code examples follow DRYKIS principles, which are:
don't repeat yourself, keep it simple
"less code" welcomes the adept
"too much code & comments" scare away everybody
if you can read code - go ahead and make it pretty
With that said, pardon my "short-hand", explanation will follow
first we need some constants and our stack-getter
const MAIN = window;
const VOID = (function(){}()); // paranoid
const HOST = `https://${location.host}`; // if not `https` then ... ?
const stak = function(x,a, e,s,r,h,o)
{
a=(a||''); e=(new Error('.')); s=e.stack.split('\n'); s.shift(); r=[]; h=HOSTPURL; o=['_fake_']; s.forEach((i)=>
{
if(i.indexOf(h)<0){return}; let p,c,f,l,q; q=1; p=i.trim().split(h); c=p[0].split('#').join('').split('at ').join('').trim();
c=c.split(' ')[0];if(!c){c='anon'}; o.forEach((y)=>{if(((c.indexOf(y)==0)||(c.indexOf('.'+y)>0))&&(a.indexOf(y)<0)){q=0}}); if(!q){return};
p=p[1].split(' '); f=p[0]; if(f.indexOf(':')>0){p=f.split(':'); f=p[0]}else{p=p.pop().split(':')}; if(f=='/'){return};
l=p[1]; r[r.length]=([c,f,l]).join(' ');
});
if(!isNaN(x*1)){return r[x]}; return r;
};
After cringing, bare in mind this was written "on the fly" as "proof of concept", yet tested and it works. Edit as you whish.
stak() - short explanation
the only 2 relevant arguments are the 1st 2, the rest is because .. laziness (short answer)
both arguments are optional
if the 1st arg x is a number then e.g. stack(0) returns the 1st item in the log, or undefined
if the 2nd arg a is either a string -or an array then e.g. stack(undefined, "anonymous") allows "anonymous" even though it was "omitted" in o
the rest of the code just parses the stack quickly, this should work in both webkit & gecko -based browsers (chrome & firefox)
the result is an array of strings, each string is a log-entry separated by a single space as function file line
if the domain-name is not found in a log-entry (part of filename before parsing) then it won't be in the result
by default it ignores filename / (exactly) so if you test this code, putting in a separate .js file will yield better results than in index.html (typically) -or whichever web-root mechanism is used
don't worry about _fake_ for now, it's in the jack function below
now we need some tools
bore() - get/set/rip some value of an object by string reference
const bore = function(o,k,v)
{
if(((typeof k)!='string')||(k.trim().length<1)){return}; // invalid
if(v===VOID){return (new Function("a",`return a.${k}`))(o)}; // get
if(v===null){(new Function("a",`delete a.${k}`))(o); return true}; // rip
(new Function("a","z",`a.${k}=z`))(o,v); return true; // set
};
bake() - shorthand to harden existing object properties (or define new ones)
const bake = function(o,k,v)
{
if(!o||!o.hasOwnProperty){return}; if(v==VOID){v=o[k]};
let c={enumerable:false,configurable:false,writable:false,value:v};
let r=true; try{Object.defineProperty(o,k,c);}catch(e){r=false};
return r;
};
bake & bore - rundown
These are failry self-explanatory, so, some quick examples should suffice
using bore to get a property: console.log(bore(window,"XMLHttpRequest.prototype.open"))
using bore to set a property: bore(window,"XMLHttpRequest.prototype.open",function(){return "foo"})
using bore to rip (destroy carelessly): bore(window,"XMLHttpRequest.prototype.open",null)
using bake to harden an existing property: bake(XMLHttpRequest.prototype,'open')
using bake to define a new (hard) property: bake(XMLHttpRequest.prototype,'bark',function(){return "woof!"})
intercepting functions and constructions
Now we can use all the above to our advantage as we devise a simple yet effective interceptor, by no means "perfect", but it should suffice; explanation follows:
const jack = function(k,v)
{
if(((typeof k)!='string')||!k.trim()){return}; // invalid reference
if(!!v&&((typeof v)!='function')){return}; // invalid callback func
if(!v){return this[k]}; // return existing definition, or undefined
if(k in this){this[k].list[(this[k].list.length)]=v; return}; //add
let h,n; h=k.split('.'); n=h.pop(); h=h.join('.'); // name & holder
this[k]={func:bore(MAIN,k),list:[v]}; // define new callback object
bore(MAIN,k,null); let f={[`_fake_${k}`]:function()
{
let r,j,a,z,q; j='_fake_'; r=stak(0,j); r=(r||'').split(' ')[0];
if(!r.startsWith(j)&&(r.indexOf(`.${j}`)<0)){fail(`:(`);return};
r=jack((r.split(j).pop())); a=([].slice.call(arguments));
for(let p in r.list)
{
if(!r.list.hasOwnProperty(p)||q){continue}; let i,x;
i=r.list[p].toString(); x=(new Function("y",`return {[y]:${i}}[y];`))(j);
q=x.apply(r,a); if(q==VOID){return}; if(!Array.isArray(q)){q=[q]};
z=r.func.apply(this,q);
};
return z;
}}[`_fake_${k}`];
bake(f,'name',`_fake_${k}`); bake((h?bore(MAIN,h):MAIN),n,f);
try{bore(MAIN,k).prototype=Object.create(this[k].func.prototype)}
catch(e){};
}.bind({});
jack() - explanation
it takes 2 arguments, the first as string (used to bore), the second is used as interceptor (function)
the first few comments explain a bit .. the "add" line simply adds another interceptor to the same reference
jack deposes an existing function, stows it away, then use "interceptor-functions" to replay arguments
the interceptors can either return undefined or a value, if no value is returned from any, the original function is not called
the first value returned by an interceptor is used as argument(s) to call the original and return is result to the caller/invoker
that fail(":(") is intentional; an error will be thrown if you don't have that function - only if the jack() failed.
Examples
Let's prevent eval from being used in the console -or address-bar
jack("eval",function(a){if(stak(0)){return a}; alert("having fun?")});
extensibility
If you want a DRY-er way to interface with jack, the following is tested and works well:
const hijack = function(l,f)
{
if(Array.isArray(l)){l.forEach((i)=>{jack(i,f)});return};
};
Now you can intercept in bulk, like this:
hijack(['eval','XMLHttpRequest.prototype.open'],function()
{if(stak(0)){return ([].slice.call(arguments))}; alert("gotcha!")});
A clever attacker may then use the Elements (dev-tool) to modify an attribute of some element, giving it some onclick event, then our interceptor won't catch that; however, we can use a mutation-observer and with that spy on "attribute changes". Upon attribute-change (or new-node) we can check if changes were made FUBU (or not) with our stak() check:
const watchDog=(new MutationObserver(function(l)
{
if(!stak(0)){alert("you again! :D");return};
}));
watchDog.observe(document.documentElement,{childList:true,subtree:true,attributes:true});
Conclusion
These were but a few ways of dealing with a bad problem; though I hope someone finds this useful, and please feel free to edit this answer, or post more (or alternative/better) ways of improving front-end security.

Dynamically modifying function call according to context

I have a web page which has four tabs at the top. Clicking one of the tabs displays the appropriate page beneath. The tab selection and display is controlled by a js/jQuery function I've called 'changeTab'. Nothing uusual there.
I want to set up a (different) JS function for each tab, to run when that tab is displayed, similar to the way jQuery 'document.ready' works when the main page itself is loaded. I can put a function call at the bottom of my 'changeTab' function, such as 'tabLoaded()'. But that obviously only calls the same one function each time.
I can name the functions 'tab_1_Loaded', 'tab_2_Loaded' etc. ,but then I need some way of dynamically modifying the function call so that the number of the tab is included (I already have the tab number, I just need to work out how to insert it into the function call).
What I am hoping for is a function call like:
tab_[insert tabNum dynamiclly here]_Loaded();
Is that possible in a few lines of code?
I have read articles on Stackoverflow, but they seem do address a different problem of creating (new?) functions with a dynamically derived name. I can be quite clear what my functions are called. I need a dynamically derived call. I suspect it may be possible with 'eval' but my reading also suggests eval is to be avoided, so I've not pursued it.
My fall-back is a series of conditionals:
if(tabNum == 1) tab_1_Loaded();
if(tabNum == 2) tab_2_Loaded();
etc.
but it seems inelegant (though simple) and it certainly works in this case where the number of possibilities is small. Is there a better way that's also simple?
LATER: I've subsequently realised there's an additional complication for the particular page/tabs I'm working on right now, (though it won't apply to the entire site). This page is for on-line booking. The first tab is the booking form (visitor enters dates, number of people). The second and subsequent tabs aren't populated until the visitor clicks 'Next' and moves on to the next stage. Consequently any function call in the 'changeTabs' function is made before the contents of the tab have actually loaded, so it does't work.
To deal with that I'm going to put the call into a script at the bottom of each tab contents. I expect there are more elegant ways of doing it, but it's only one line of code, whereas all the offered solutions are actually more verbose (and harder for me to understand). I will probably still need the call from 'changeTab' to cope with the visitor flicking through the tabs before finalising the booking.
When press the tab, the call back function will always be invoked, no matter what how many call back functions all will be invoked. You cannot conditionally invoke a callback function from a key press. Ideal way to implement this would be
i) Have a single call back function for the tab event
ii) Identify the id of the element, that is currently on focus when tab is pressed inside that call back function
iii) Add conditions based on that element on focus to have your logic of functions for respective elements
Yes it's possible to do what you're asking. All functions and variables declared with global scope are methods and properties of the window object, so you can build the name of the your function as a string and reference it via bracket notation.
So assuming you have tabNumber already stored in a variable:
var functionName = "tab_"+tabNumber+"_Loaded";
window[functionName]();
(See https://codepen.io/slynagh/pen/MMVEoE)
But a better approach would be to use callback functions or else use one tab_Loaded() function which accepts the tabNumber as a parameter, eg:
function tab_Loaded(tabNumber){
if(tabNumber === 1) { do something }
else if(tabNumber === 2 ) {do something else}
//etc
}

How can I delay s.t call in adobe DTM?

I have created one event based rule-
In adobe analytics portion I have set s.t call but how can I delay this call - “s.t call”
Here i have attached one screenshot where i actually set.
enter image description here
DTM does not currently offer a way to delay the s.t (or s.tl) trigger in Event Based Rules (EBR), so you will have to write your own javascript that ultimately makes the s.t call.
Since your screenshot shows the Adobe Analytics (AA) section in your EBR, it sounds like you have AA setup as a Tool in DTM. DTM lets you reference the AA s object within the custom code section of the AA section of a tool, but it doesn't have an official way to get a reference of AA s object outside of that scope.
So in order to achieve this, you have to put the s object into the global (window) scope in the Tool config. You can do this by adding a line in the custom code box of the AA Tool config
window.s = s;
(or if you changed the namespace to something else in the tool config, use that instead).
Next, a note about using this - I noticed in your screenshot for Page Name you have %this.getAttribute(data-search-pagename)%. Because you want to delay the s.t call, whatever you are doing (e.g. a setTimeout, popping it inside some ajax callback function, etc.) will almost certainly take you out of the scope where this properly references the element the EBR is based off of, so you will no longer be able to use this to populate the page name.
To get around this, go to the Conditions section of the EBR. Under Rule Conditions > Criteria, choose Data : Custom. In the Custom code box, you can add the following:
_satellite.setVar('data_search_pagename',this.getAttribute('data-search-pagename'));
return true;
The above will push the data-search-pagename attribute value to a Data Element named 'data_search_pagename' (or name it whatever you want, according to your conventions). The return true; part will always make this condition true, so that it is "invisible" to your rule and not actually affect whether or not the rule should trigger.
Finally, in the EBR, you will not use the AA tool section (choose the disabled option in that section). Instead, you will need to create and add your code in the Javascript / Third Party Tags section of the EBR. You will need to set whatever AA vars you had in the AA section as regular javascript variables, and for the Data Element %data_element% syntax, instead use the _satellite.getVar('data_element') javascript syntax .
I'm not sure what kind of delay you are looking to do, but here's a 100ms delay as an example (based on what is shown in your screenshot):
window.setTimeout(function() {
var s = window.s;
s.pageName = _satellite.getVar('data_search_pagename');
s.channel = _satellite.getVar('util_channel');
s.t();
},100);
If you have lots of these types of events on your site where the page view call needs to be more controlled, I would recommend using a direct call rule and aborting the initial AA PV call. In fact, it is common to create a "global page load" rule for single page applications (SPA) or for when you are waiting for ajax to return search results - for example
Here's a jQuery example of using a direct call rule within a search results page rule that will fire after ajax has completed and returned:
Javascript: jQuery - wait for callback and call DC rule
/* Ajax Detection on SRP */
$(document).ajaxComplete(function( event, xhr, settings ) {
_satellite.track('my_pageView_rule_here')
})
Hope this helps.

What happens if a JavaScript event-listener is called and target element is missing?

For the moment, we're loading site-wide event-listeners from a single common.js file for a Rails project. We're aware of (most of) the trade-offs involved there, and are just trying to mitigate them. Once our basic architecture takes hold, we may move them off to separate files by controller or by view.
For the moment, the quick question is how we can activate them only when necessary, which begs the mangled, pseudo-zen question:
if an event-listener is declared in a forest when nobody is around to hear it, does it still make a sound?
In other words, if one declares a basic listener (i.e., nothing persistent like .live() or .delegate()) in the JavaScript for a given page, and the target element is not actually present on that given page, does anything really happen, other than the few cycles devoted to evaluating it and checking the DOM for the element? Is it active in memory, looking for that element? Something else? It never seems to throw an error, which is interesting, given that in other contexts a call like that would generate a null/nil/invalid type of error.
For instance:
$(document).ready(function () {
$('#element').bind('blur keyup', function);
}
Assume that #element isn't present. Does anything really happen? Moreover is it any better to wrap it in a pre-filter like:
$(document).ready(function () {
if ($('#element')) {
$('#element').bind('blur keyup', function);
}
Or, are the .js interpreters in the browsers smart enough to simply ignore a basic listener declared on an element that's not present at $(document).ready? Should we just declare the initial, simple form above, and leave it at that, or will checking for the element first somehow save us a few precious resources and/or avoid some hidden errors we're not seeing? Or is there another angle I'm missing?
JQuery was designed to work with 0+ selected elements.
If no elements were selected, nothing will happen.
Note that you will never get null when using jQuery selector. For example:
$('#IDontExist') // != null
$('#IDontExist').length === 0 // true (it's ajQuery object with
// zero selected elements).
The docs says:
If no elements match the provided selector, the new jQuery object is "empty"; that is, it contains no elements and has .length property of 0.
$('#element') if results into empty set then jQuery will not do anything.
Since jQuery always returns an object we can can call the methods on an empty set also but internally it will do the checking before applying it's logic.
Even if you want to check if the element exists before attaching the event handler you can use length property of jQuery object.
if ($('#element').length > 0) {
$('#element').bind('blur keyup', function);
}

What is common AJAX convention for knowing a particular state when a page is loaded?

This has been a question I've had since I started doing serious ajax stuff. Let me just give an example.
Let's say you pull a regular HTML page of a customer from the server. The url can look like this:
/myapp/customer/54
After the page is rendered, you want to provide ajax functionality that acts on this customer. In order to do this, you need to send the id "54" back to the server in each request.
Which is the best/most common way to do this? I find myself putting this in hidden form forms. I find it easy to select, but it also feels a bit fragile. What if the document changes and the script doesn't work? What if that id gets duplicated for css purposes 3 months from now, and thus breaks the page since there are 2 ids with the same name?
I could parse the url to get the value "54". Is that approach better? It would work for simple cases repeatedly. It might not work so well for complex cases where you might want to pass multiple ids, or lists of ids.
I'd just like to know a best practice - something robust that is clean, elegant and is given 2-thumbs up.
I think the best way to do this is to think like you don't have Ajax.
Let's say you have a form which is submitted using Ajax. How do you know what URL to send it to?
The src attribute. Simply have your script send the form itself. All the data is in the form already.
Let's say you have a link which loads some new data. How do you know the URL and parameters?
The href attribute. Simply have the script read the URL.
So basically you would always read the URL/data from the element being acted upon, similar to what the browser does.
Since your server-side code knows the ID's etc. when the page is being loaded, you can easily generate these URLs there. The client-side code will only need to read the attributes.
This approach has more than just one benefit:
It makes it simpler where the URLs and data is stored, because they are put exactly in the attributes that you'd normally find then in HTML.
It makes it easier to make your code work without JavaScript if you want to, because the URLs and all are already in places where the browser can understand them without JS.
If you're doing something more complex than links/forms
In a case where you need to allow more complex interactions, you can store the IDs or other relevant data in attributes. HTML5 provides the data-* attributes for this purpose - I would suggest you use these even if you're not doing HTML5:
<div data-article-id="5">...</div>
If you have a more full-featured application on the page, you could also consider simply storing your ID in JS code. When you generate the markup in the PHP end, simply include a snippet in the markup which assigns the ID to a variable or calls a function or whatever you decide is best.
Ideally your form should work without javascript, so you probably have a hidden form input or something that contains the id value already. If not, you probably should.
It's all "fragile" in the sense that a small change will affect everything, not much you can do about that, but you don't always want to put it in the user's hands by reading the url or query string, which can be easily manipulated by the user. (this is fine for urls of course, but not for everything. Same rules that apply to trusting $_GET and query strings apply here).
Personally, I like to build all AJAX on top of existing, functional code and I've never had a problem "hooking" into what is already there.
Not everything is a form though. For
example, let's say you click a "title"
and it becomes editable. You edit it,
press enter, and then it becomes
uneditable and part of the page again.
You needed to send an ID as part of
this. Also, what about moving things
around and you want those positions
updated? Here's another case where
using the form doesn't work because it
doesn't exist.
All of that is still possible, and not entirely difficult to do without javascript, so a form could work in either case, but I do indeed see what you're saying. In almost every case, there is some sort of unique id, whether it's a database id or file name, that can be used as the "id" attribute of the html that represents it. * Or the data- attribute as Jani Hartikainen has mentioned.
For instance, I have a template system that allows drag/drop of blocks of content. Every block has an id and every area that it can get dropped has one as well. I will use prefixes on the containing div id like "template-area_35" or "content-block_264". In this case, I don't bother to fallback w/o javascript, but it could be done (dropdown-> move this to area for example). In any case, it's a good use of the id attribute.
What if that id gets duplicated for
css purposes 3 months from now, and
thus breaks the page since there are 2
ids with the same name?
If that happens (which it really shouldn't), someone is doing something wrong. It would be their fault if the code failed to work, and they would be responsible. Ids are by definition supposed to be unique.
IMHO putting is at a request parameter (i. e. ?customerId=54) would be good 'cos even if you can't handle AJAX (like in some old mobile browsers, command-line browsers and so) you can still have a reference to the link.
Apparently you have an application that is aware of the entity "Customer", you should reflect this in your Javascript (or PHP, but since you're doing ajax I would put it in Javascript).
Instead of handmaking each ajax call you could wrap it into more domain aware functions:
Old scenario:
var customer_id = fetch_from_url(); // or whatever
ajax("dosomething", { "customer": customer_id }, function () {
alert("did something!");
});
ajax("dosomethingelse", { "customer": customer_id }, function () {
alert("did something else!");
});
New scenario:
var create_customer = function (customer_id) {
return {
"dosomething" : function () {
ajax("dosomething", { "customer": customer_id }, function () {
alert("did something!");
});
},
"dosomethingelse": function () {
ajax("dosomethingelse", { "customer": customer_id }, function () {
alert("did something else!");
});
}
};
}
var customer_id = fetch_from_url(); // or whatever
var customer = create_customer(customer_id);
// now you have a reference to the customer, you are no longer working with ids
// but with actual entities (or classes or objects or whathaveyou)
customer.dosomething();
customer.dosomethingelse();
To round it up. Yes, you need to send the customer id for each request but I would wrap it in Javascript in proper objects.

Categories

Resources