I have an array of data. I have put this data on my site in different places over different attributes, how innerHTML value placeholder etc.
Is it possible to link this values with the array from where I can take data? So that when I change the data in array, it going automatic changed on the site?
Also I try to show how I did it mean:
var test = Array();
test['place1'] = 'NY';
var myspan = document.createElement('span');
myspan.innerHTML = test['place1'];
On some event the value of test['place1'] is changed to 'LA', and at the same moment the value of myspan.innerHTML must be changed too.
Native JS only please.
This needs to be manually managed. A simple solution would be something like this:
function Place(container, initVal) {
this.container = container ? container : {};
this.set(initVal);
}
Place.prototype.place = "";
Place.prototype.get = function() {
return this.place;
}
Place.prototype.set = function(val) {
this.place = val;
this.container.innerHTML = val;
}
var test = {}; // object
test['place1'] = new Place(document.createElement('span'), "NY")
test['place1'].set('New Value');
This is not a full-feature solution, but gives you an idea of the coordination that needs to take place.
If you're only supporting modern browsers, then the syntax can be cleaned up a bit by using getters/setters.
In the future, you'll be able to use Proxy, which will make it even easier and cleaner.
There is no native way to bind an attribute of an HTML element to the values of an array, but you aren't actually using an array; you're using an object, and it is a simple matter to define special features on an object. For example:
First, define your object:
function boundArray(){
this._bindings = {};
this.setBinding = function(key,element){
this._bindings[key] = element;
};
this.setValue = function(key,value){
this[key] = value;
if(this._bindings[key]){
this._bindings[key].innerHTML = value;
}
}
}
Then use it in your code:
// create a new instance of the boundArray
var test = new boundArray();
// create the HTML element to use, and add it to the DOM
var myspan = document.createElement('span');
document.body.appendChild(myspan);
// bind the HTML element to the required key in the boundArray
test.setBinding('place1',myspan);
// Now every time you set that key on the boundArray (using setValue), it will also change the innerHTML field on the element
test.setValue('place1','NY');
// You can access your information from the boundArray in the usual ways:
var somevar = test.place1;
var anothervar = test['place1'];
What you are talking about is an MVVM solution. Most MVVM JavaScript solutions uses some object that represents an observable, which is a field within the object. When the value in the object changes, the observable lets the framework know to update the DOM. It also listens to the DOM for change events, and updates the object in reverse. For arrays, it's a similar process: it listens for adds or removes of the array, and updates the UI accordingly.
As #MCL points out in the comments on this post below, there is a way to watch changes to an object, and it isn't overly difficult to generically attach to an element on the DOM. However, There are a lot of good frameworks out there that make this REALLY easy, so that may be something to consider.
Related
When defining a method on a user defined class that includes an HTML element JS throws a TypeError saying object.method is not a function.
My hunch is when only returning document.createElement the class inherits HTML object methods that prevent user defined methods from working? So my thought was to contain the HTML object as an element within an array, but still no dice.
class inputBox {
constructor(attributeList){
let element = document.createElement('input')
//just creating a unique ID here
attributeList.id = Math.random().toString(36).substr(2, 16);
for (let attr in attributeList){
this[attr] = attributeList[attr]
element.setAttribute(attr,this[attr])
}
return [element,attributeList]
};
updateValue(newValue) {
let element = document.querySelector(`#${this[1].id}`)
return element.value = newValue
};
}
this works fine
document.body.appendChild(inputBox1[0])
this not so much (note: there is no [0] since I'm querying the DOM)
inputBox1.updateValue("hello")
The idea is you could call something like the following with multiple parameters
var inputBox1 = new inputBox({type:'email', placeholder:'Your email'})
I think this is achievable with jquery, but was attempting a vanilla approach as learning exercise. Thx
Whenever you explicitly return an object from a constructor, the return value will be just that object, and not an instance of the class. When you do
return [element,attributeList]
what is returned is just a plain array, without anything connected to an inputBox, so proceeding to reference an inputBox class method on that array won't work.
Instead, put the element and attributeList onto the instance.
By putting the element onto the instance, you can also avoid creating a dynamic unique ID for each element (which is a code smell) - rather, just reference the this.element:
class inputBox {
constructor(attributeList){
this.element = document.createElement('input');
this.attributeList = attributeList;
for (const attr in attributeList){
this.element.setAttribute(attr,this.attributeList[attr]);
}
}
updateValue(newValue) {
this.element.value = newValue;
}
}
Still, unless there's going to be more code in the inputBox, there doesn't seem to be any use of the class here - after construction, its only exposed functionality is to set the value of the input, which could be done less obscurely by just using a reference to the input. Consider having a function that assigns the attributeList to the element's attributes, and then just using the plain element, eg:
const myInput = assignAttributes(attributeList);
// ...
myInput.value = 'newValue';
I use a lot of the following expressions in my code:
document.
.getElementsBy...
.querySelector...
I need to save characters without using any libraries. That can be done by
var d = document;
Then, instead of document. I can write d. now.
I am wondering if there is a simple way to do the same thing for methods
.getElementsBy... and .querySelector....
Since these have a variable term, I cannot put the entire thing into a
variable, like var q = .querySelector(".class"), because the .class
changes almost every time.
You can create functions to avoid adding properties to the document object as shortcut if you don't want to.
function gEBI(d,id)
{
return d.getElementById(id);
}
function qS(d,s)
{
return d.querySelector(s);
}
var d = document;
var ele1 = gEBI(d,"yourID");
var ele2 = qS(d,".class");
You can make your own shortcut functions-references manually.
document.gEBI = document.getElementById;
document.gEBI(id);
But it's not a good practice to make such shortcuts.
I need to pass some html code as a parameter, however, before I pass it, I need to change some src attribute values.
I cannot use lastIndexOf or any of those to modify the html value since I don't know which value the src's will have.
What I'm trying to do then, is to create an object containing the html, and then alter that object only. I don't want to alter the actual webpage.
is this possible??
What I did first was this:
$('[myImages]').each(function() {
var urlImg = "../tmpFiles/fileName" + counter;
$(this).attr('src', urlImg);
counter++;
});
So finally, I had the desired code like this:
myformData = { theChartCode: $('#TheDivContainingTheHTML').html() }
However, this actually changes the image sources on the webpage, which I don't want to.
Then I thought I could create a JQuery object with the html so I could alter that object only like this:
var $jQueryObject = $($.parseHTML($('#TheDivContainingTheHTML').html()));
But now, I can't figure out how to iterate within that object in order to change the src attribute's values of the desired images.
Any help will be really appreciated ;)
There are several ways to do It. First would be creating a clone of target element and use the same on the Fly. You can do like below:
var Elem = $('#TheDivContainingTheHTML').clone();
now do whatever you want like iterate, alter,insert,remove.
var allImages =$(Elem).children("img");
Thanks Much!
Depending on when you want to change the object, solution will be different. Let's pretend you want to change it after you click another element in the page. Your code will look like that :
var clonedHTML;
$('#clickable-element').click(function() {
var $originalHTML = $(this).find('.html-block');
var $cloneHTML = $originalHTML.clone();
$cloneHTML.find('.my-image').attr('src', 'newSrcValue');
clonedHTML = $cloneHTML.clone();
return false; //Prevents click to be propagated
});
//Now you can use `clonedHTML`
The key point here is the clone method : http://api.jquery.com/clone/.
You can clone the elements:
var outerHTML = $collection.clone().attr('src', function(index) {
return "../tmpFiles/fileName" + index;
}).wrapAll('<div/>').parent().html();
You can also use the map method:
var arr = $collection.map(function(i) {
return $(this).clone().attr('src', '...').prop('outerHTML');
}).get();
I have all my elements, on a project, that are being transferred with data attributes so the javascript can know what to do.
But I have one problem, one part of the application must get all the created variables to compare time, and I have they saved like (data-product-created, data-category-created, data-link-created, etc...). It would be a huge headache if I had to put them manually on the jQuery selector...
Do jQuery has some method to search custom data attributes existence?
Like: element[data-(.*)-created]
You could create a low level Javascript method to loop through elements and pull their created values:
var getCreateds = function($els) {
var returnSet = [];
$els.each(function(i, el){
var elDict = {'el': el};
$.each(el.attributes, function(i, attr){
var m = attr.name.match(/data\-(.*?)\-created/);
if (m) elDict[m[1]] = attr.value;
});
returnSet.push(elDict);
});
return returnSet;
};
See demo
Based on mVChr answer (THANK YOU), i've rewritten the code on a simple, better and clean code:
jQuery('div').filter(function(){
for( var i in this.attributes ){
if(typeof this.attributes[i] !== 'object') continue;
if(this.attributes[i].name.match(/data\-(.*?)\-created/)) return true;
}
return false;
})
Changes:
+ Verifying attribute type... some attributes are browser internal functions or callbacks.
~ Using filter handler from jQuery, its better and cleaner way.
~ Using for instead of $.each, faster indeed.
A simple question I'm sure, but I can't figure it out.
I have some JSON returned from the server
while ($Row = mysql_fetch_array($params))
{
$jsondata[]= array('adc'=>$Row["adc"],
'adSNU'=>$Row["adSNU"],
'adname'=>$Row["adname"],
'adcl'=>$Row["adcl"],
'adt'=>$Row["adt"]);
};
echo json_encode(array("Ships" => $jsondata));
...which I use on the client side in an ajax call. It should be noted that the JSON is parsed into a globally declared object so to be available later, and that I've assumed that you know that I formated the ajax call properly...
if (ajaxRequest.readyState==4 && ajaxRequest.status==200 || ajaxRequest.status==0)
{
WShipsObject = JSON.parse(ajaxRequest.responseText);
var eeWShips = document.getElementById("eeWShipsContainer");
for (i=0;i<WShipsObject.Ships.length;i++)
{
newElement = WShipsObject.Ships;
newWShip = document.createElement("div");
newWShip.id = newElement[i].adSNU;
newWShip.class = newElement[i].adc;
eeWShips.appendChild(newWShip);
} // end for
}// If
You can see for example here that I've created HTML DIV elements inside a parent div with each new div having an id and a class. You will note also that I haven't used all the data returned in the object...
I use JQuery to handle the click on the object, and here is my problem, what I want to use is the id from the element to return another value, say for example adt value from the JSON at the same index. The trouble is that at the click event I no longer know the index because it is way after the element was created. ie I'm no longer in the forloop.
So how do I do this?
Here's what I tried, but I think I'm up the wrong tree... the .inArray() returns minus 1 in both test cases. Remember the object is globally available...
$(".wShip").click(function(){
var test1 = $.inArray(this.id, newElement.test);
var test2 = $.inArray(this.id, WShipsObject);
//alert(test1+"\n"+test2+"\n"+this.id);
});
For one you can simply use the ID attribute of the DIV to store a unique string, in your case it could be the index.
We do similar things in Google Closure / Javascript and if you wire up the event in the loop that you are creating the DIV in you can pass in a reference to the "current" object.
The later is the better / cleaner solution.
$(".wShip").click(function(){
var id = $(this).id;
var result;
WShipsObject.Ships.each(function(data) {
if(data.adSNU == id) {
result = data;
}
});
console.log(result);
}
I could not find a way of finding the index as asked, but I created a variation on the answer by Devraj.
In the solution I created a custom attribute called key into which I stored the index.
newWShip.key = i;
Later when I need the index back again I can use this.key inside the JQuery .click()method:
var key = this.key;
var adt = WShipsObject.Ships[key].adt;
You could argue that in fact I could store all the data into custom attributes, but I would argue that that would be unnecessary duplication of memory.