jquery storing dynamic innerhtml into usable jquery variable - javascript

var = cooldynamicelement
How could I store the inner html I grab with jQuery from my div ie. <div class="username"> </div> to store as an accessible variable in jQuery eg. cooldynamicelement so I can grab and use at different areas of my site by just calling ie. $cooldynamicelement and updates with the dynamic .username element value.

1. Store HTML into localStorage
var dynamicElementHTML = localstorage.dynamicElementHTML || $(".username").html() || "";
localstorage["dynamicElementHTML"] = dynamicElementHTML;
To make it available to other pages a way would be to use the power of localstorage
https://developer.mozilla.org/en/docs/Web/API/Window/localStorage
If you're actually interested in the whole element (not only it's inner HTML) than instead of .html() use .prop("outerHTML")
2. Binding using jQuery (essential idea)
If you only want a way to reflect some variable HTML as actual html and make it alive you could do like:
var $myElement = $("<div />", {
class : "userData",
append : $someDynamicElements,
appendTo : $someParentElement,
on : {
contentUpdate : function() {
$(this).html( $someDynamicElements );
}
}
});
than whenever your $someDynamicElements changes you can trigger a contentUpdate
$myElement.trigger("contentUpdate")
3. Binding using jQuery (concept)
Here's the same elements binding concept gone wild:
// Here we will store our elements
var EL = {};
// Create desired HTML elements like this:
var LIST = {
username: $("<b/>", {
html : "UNKNOWN",
click : function() {
alert( $(this).text() );
}
}),
email: $("<a/>", {
html : "test#test.test",
href : "mailto:"+ "test#test.test"
}),
// add more here, you got the idea.
// don't forget that you can assign any JS / jQuery propery to your element.
// You can go insane using .on() and later .trigger()
};
// Our small "program" that replaces data-bind elements
// with dynamic elements from our list
$("[data-bind]").replaceWith(function(i){
var bind = this.dataset.bind;
if(!LIST[bind]) return;
if(!EL.hasOwnProperty(bind)) EL[bind] = [];
var klon = LIST[bind].clone(true)[0];
EL[bind].push(klon);
return klon;
});
// That's it. Now goes your code ///////////////
$(EL.username).css({color:"red"}); // just to test if it works :D
$("[data-target]").on("input", function(){
var target = this.dataset.target;
$(EL[target]).html( this.value );
});
// P.S: Even having thousands of elements inside EL
// say you have "EL.tableRows" you can do fabulously
// quick stuff like i.e: sorting, cause you iterate over a plain JS array.
// After the sorting of EL.tableRows is done and you need a jQuery
// representation simply use $(EL.tableRows).
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Dynamic element Binding in jQuery</h2>
Enter some text and see the update trigger in different places<br>
<input data-target="username"><br>
Welcome <span data-bind="username"></span> !!<br>
You name is <span data-bind="username"></span> Click the red text!<br>
<span data-bind="email"></span>

Well if you want to have the jqueryObject in a variable, just do this:
$(function(){
window.$cooldynamicelement = $("div.username");
})
that way you're able to use $cooldynamicelement in a global context. If is that what you want. This way you're saving a reference to your .username element and thus every time you use it will be updated.
NOTE: If you decide to do this, be careful with polluting your global context.:

Related

Get element data-attributes and insert them into the button

I want to ensure that when I click on the divs (A, B, C), the link of the button changes and gets the values of the data attributes in the appropriate places. I wrote a small script, but it does not work, and there is still not enough knowledge to understand exactly where I went wrong. Any help would be welcome.
document.getElementById("product").onclick = function() {
document.getElementById("purchase").href =
"/?add-to-cart=" + this.data-product +
"&variation_id=" + this.data-id + "/";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="product__items" id="product">
<div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div>
<div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div>
<div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div>
<div class="product__items---btn">
Button
</div><!-- btn -->
</div>
You have several problems here.
First, I suggest you consult the documentation for HTMLElement.dataset or jQuery's .data().
Also, if you intend on using event delegation, you can't use this to refer to the event source element in a vanilla event listener as it will refer to the delegate.
Since you do have jQuery involved, you might as well use it since it makes this a lot easier (see also vanilla JS version below)
const button = $("#purchase")
$("#product").on("click", ".items-uniqueItem[data-id][data-product]", function() {
// Due to the selector above, `this` is now the clicked `<div>`
// Extract data properties
const { product, id } = $(this).data()
// Construct URL parameters
const params = new URLSearchParams({
"add-to-cart": product,
"variation_id": id
})
// Set the `href`
button.prop("href", `/?${params}/`)
})
/* this is just for visibility */
.items-uniqueItem{cursor:pointer;}#purchase{display:block;text-decoration:none;margin: 1rem;}#purchase:after{content:attr(href);display:block;color:#ccc;margin:.5rem;}
<!-- your HTML, just minified -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><div class="product__items" id="product"><div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div><div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div><div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div><div class="product__items---btn">Button</div></div>
A vanilla JS version would look something more like this. You can use Element.closest() to locate the delegated event source
const button = document.getElementById("purchase")
document.getElementById("product").addEventListener("click", e => {
// find the required event source element
const el = e.target.closest(".items-uniqueItem[data-id][data-product]")
if (el) {
// Extract data properties
const { product, id } = el.dataset
// Construct URL parameters
const params = new URLSearchParams({
"add-to-cart": product,
"variation_id": id
})
// Set the `href`
button.href = `/?${params}/`
}
})
.items-uniqueItem{cursor:pointer;}#purchase{display:block;text-decoration:none;margin: 1rem;}#purchase:after{content:attr(href);display:block;color:#ccc;margin:.5rem;}
<!-- your HTML, just minified -->
<div class="product__items" id="product"><div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div><div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div><div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div><div class="product__items---btn">Button</div></div>
As you can see, it's not very different to the jQuery version so maybe you might not need jQuery
I've never personally used the element.onlick = function() {...} notation, so I'll be usingelement.addEventListener('click', (e) => ...), but it should work the same way.
What you are doing is selecting the object that has the id "product". But "product" is the parent os the elements you want to select.
If you want to select several elements and do something with them, you can't use the id attribute, since id is unique for html page. So you'll want to use classes for that.
Create a class and add that class to each child (the ones with the data-product).
Select all children with .querySelectorAll(). Here is the doc. This returns a NodeList, but it's similar to an Array.
Iterate thought the List with a .forEach(item => ...) where item represents each element of the list.
Add an Event Listener (or .click, I guess) on each item.
*theList*.forEach( (item) => {
item.addEventListener('click', (event) => {
event.target.href = "/?add-to-cart=" + event.target.dataset.product + "&" + "variation_id=" + event.target.dataset.id + "/";
})
));
To access a dataset in JS you use the .dataset property.
First, grab all the divs that have a given class so that we can use their data.
const items = document.querySelectorAll('.items-uniqueItem');
items.forEach(item => item.addEventListener('click', (e) => console.log(e.target)))
Then inside you click handler you can get the button reference and assign the properties you want to get from it.

Passing in right information when rendering items from Javascript on the HTML side

I am trying to create an extension for VSTS using their extension kit (https://learn.microsoft.com/en-us/vsts/extend/overview?view=vsts).
<script type="text/javascript">
VSS.init();
var items = {}
// Get data service and display
VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService) => {
dataService.getDocuments('MyCollection2').then((docs) => {
// keep a reference to the element instead of searching for it in each loop.
const itemsDiv = document.getElementById('items');
const contents = [];
for (let i = 0; i < docs.length; i++) {
// using template strings here to show you another way of working with strings in es6
var name = docs[i].name
contents.push(
`<div
class="listItem"
onClick="console.log(docs[i])"
onmouseover="this.style.background='#D5DBDB';"
onmouseout="this.style.background='white';">
${docs[i].name}
</div>`
)
}
// finally update the target element one time with your contents.
// The new line character isn't required, can just use '',
// but this might be easier to read for you
itemsDiv.innerHTML = contents.join('');
});
});
</script>
So what my javascript part does is I try to fetch objects from VSTS`s internal data storage (I named it MyCollection2) and display the objects as a list
HTML part
<section>
<nav>
<div class="create_button">+ Create KPI</div>
<div id="items"></div>
</nav>
<article>
<h2>Create KPI</h2>
<br>
<form action="" id="form" onsubmit="sConsole(event)">
KPI Name<br>
<input type="data" id="name">
<br><br>
Actual Value<br>
<input type="data" id="actual">
<br><br>
Potential Value<br>
<input type="data" id="potential">
<br><br>
Goal %<br>
<input type="data" id="goal">
<br><br>
<button type="submit">Create</button><span>Cancel</span>
</form>
</article>
</section>
So all the objects are rendered in the div with the id items.
Everything is fine up to this point.
The problem is the onClick="console.log(docs[i]) part in my javascript part.
My intention was to console.log the document object whenever each item in the list was clicked.
However, this doesn't print the object as I intended.
It just prints externalContentHost10 and I don't know what that is.
What can I do to make this work?
docs is defined in your function; the scope of the onclick attribute (note: should be all lowercase) is not the same. In general, you should avoid inline event handlers as they’re not very flexible or maintainable. You should instead use addEventListener, which means ditching innerHTML and working with proper element nodes. A few other changes I would make are:
Flattening the promises (removing the nesting) by returning them
Using for...of for iteration
Using const (and let, but in this case const is enough) instead of var so that your variables have the right scope
This gives us:
VSS.init();
const items = {};
// Get data service and display
VSS.getService(VSS.ServiceIds.ExtensionData)
// the callback on the next line returns a promise, which the JavaScript engine will follow, so you don't need to nest the next `then`
.then((dataService) => dataService.getDocuments('MyCollection2'))
.then((docs) => {
// keep a reference to the element instead of searching for it in each loop.
const container = document.getElementById('items');
// this loop will remove any existing children
while (container.firstChild !== null) {
container.removeChild(container.firstChild);
}
// `for...of` is a simpler way to iterate over a collection
for (const doc of docs) {
// create a `div` element
const div = document.createElement("div");
// add a text node to it
div.appendChild(document.createTextNode(doc.name));
// add event listeners to change its background
div.addEventListener("mouseover", e => { div.style.background = "#D5DBDB"; });
div.addEventListener("mouseout", e => { div.style.background = "white"; });
// add a `click` listener
div.addEventListener("click", e => { console.log(doc); });
// add the new div to the container
container.appendChild(div);
}
});
If you wanted to use classes instead to manage the styling—which is the recommended method—then you could implement the event listeners using classList:
div.addEventListener("mouseover", e => div.classList.add("hover-class"));
div.addEventListener("mouseout", e => div.classList.remove("hover-class"));
(classList has toggle and replace methods, but they aren’t supported by IE at all, and Edge only seems to support toggle, so whether to use them depends on your minimum supported version.)
But you would probably be better off defining a CSS :hover class rather than doing all this, if styling is all you want to change.

jQuery read data from event

Here I have a problem:
I created one div and passed some data into it:
var div = $("<div />");
$.data(div, "a", 1);
div.on("click", function(event) {
console.log($.data(event.target, "a")); // print undefined
});
It looks like I could not retrieve data bound with an UI element this way. I would like to know why and whether there is any alternative for this - get a piece of data associated with an UI element within an event.
Thanks!
Try this ;-)
var div = $("<div>clic</div>").appendTo('body');
div.data("a", 1);
div.on("click", function() {
console.log($(this).data("a"));
});
JsFiddle : http://jsfiddle.net/t3n8y2xz/

onclick event won't fire when there is more than one dynamically added button

So I have EDIT and REMOVE buttons that are dynamically added for each data node (a "poll") in a Firebase database. I have a function which assigns onclick listeners to these with jQuery, but oddly, the event only fires when there just happens to be a single node, and hence a single pair of EDIT/REMOVE buttons. When there are multiple nodes and multiple pairs of buttons, none will fire. Here's the javascript where the events are added to the buttons...
function displayCurrentPollsForEditing(pollsRef)
{
var tbl = createTable();
var th = ('<th>Polls</th>');
$(th).attr('colspan', '3');
$(th).appendTo($(tbl).children('thead'));
pollsRef.once('value', function(pollsSnapshot) {
pollsSnapshot.forEach(function(pollsChild) {
var type = pollsChild.name();
// If this is true if means we have a poll node
if ($.trim(type) !== "NumPolls")
{
// Create variables
var pollRef = pollsRef.child(type);
var pollName = pollsChild.val().Name;
var btnEditPoll = $('<button>EDIT</button>');
var btnRemovePoll = $('<button>REMOVE</button>');
var tr = $('<tr></tr>');
var voterColumn = $('<td></td>');
var editColumn = $('<td></td>');
var rmvColumn = $('<td></td>');
// Append text and set attributes and listeners
$(voterColumn).text(pollName);
$(voterColumn).attr('width', '300px');
$(btnEditPoll).attr({
'class': 'formee-table-button',
'font-size': '1.0em'
});
$(btnRemovePoll).attr({
'class': 'formee-table-remove-button',
'font-size': '1.0em'
});
$(btnEditPoll).appendTo($(editColumn));
$(btnRemovePoll).appendTo($(rmvColumn));
// Append to row and row to table body
$(tr).append(voterColumn).append(editColumn).append(rmvColumn);
$(tr).appendTo($(tbl).children('tbody'));
// Append table to div to be displayed
$('div#divEditPoll fieldset#selectPoll div#appendPolls').empty();
$(tbl).appendTo('div#divEditPoll fieldset#selectPoll div#appendPolls');
$(btnEditPoll).click(function() {
displayPollEditOptions(pollRef);
return false;
});
$(btnRemovePoll).click(function() {
deletePoll($(this), pollsRef);
return false;
});
}
});
});
}
The markup would be something like the following...
<div id="divEditPoll">
<form class="formee" action="">
<fieldset id="selectPoll">
<legend>SELECT A POLL</legend>
<div class="formee-msg-success">
</div>
<div class="grid-12-12" id="appendPolls">
</div>
</fieldset>
</div>
EDIT - So I've switched some lines around and now I don't set the click() events until the buttons are appended to the document, so the button elements are definitely in the DOM when the click events are attached. So could this issue result from not setting id's for these buttons? That seems strange to me, since I'm using variable references rather than ids to attach the events.
There are two things I would check for.
First, make sure you don't have two elements with the same id. If you do, jquery may only bind to the first, or not bind at all.
Second, make sure the element is added to the dom before jquery attempts to bind the click event. If the code is running asynchronously, which can easily happen if you're using ajax, then you may be trying to bind the event before creating the element. Jquery would fail to find the element then give up silently.
you should use .on() for dynamically added button

Looping through generated HTML with jQuery

I know if I wanted to bind events to generated HTML, I'd need to use something like .on(), but I've only used it when binding events like .click().
I'm creating a web app that applys a list of colors. Colors are generated from a JSON file. Once fetched, I add it to the page, with certain information contained in attributes. I'd like to do something with the new generated HTML, which is list-elements. But what console.log() is showing me is there is nothing in the parent ul. Even though on the page I see the newly added content.
Here's the entire code based around it.
var setColors = function(){
getColors = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
//console.log(colors);
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
addColors();
}
addColors = function(){
var el = $('#picker').children;
$(el).each(function(){
console.log($(this));
});
}
return getColors();
}
$(function(){
setColors();
});
addColors() is where I'm having trouble with. The error says 'Uncaught TypeError: Cannot read property 'firstChild' of null. How can I work with the newly generated HTML?
You are missing parentheses on the children method:
var el = $('#picker').children();
Also, if you want the addColor method to be executed on the newly generated html, then you must add a call to it after the html is generated, from within the getJSON callback method.
addColors = function(){
var el = $('#picker').children;
$(el).each(function(){
console.log($(this));
});
}
A few issues:
missing end semi-color
missing parentheses on .children()
children() returns a jQuery object, no need for $(el)
Updated:
window.addColors = function(){
var $el = $('#picker').children();
$el.each(function(){
// do stuff here, but could attach each() to above, after children()
});
};

Categories