what is the correct method to append a div to dom, and keep a handle for manipulating that dom later ? and case removing it will the refrence be deleted also ? if not how can i ?
this is an example code of what i came out with, please let me know your opinon and if there is a better more solid method for doing this.
note that i keep reference in an array because many elements can manipulate element.
var layovers=[];
function ajaxMe($e){
//do some ajax call
var lay=layoverThis($e);
layovers[lay].remove(); //does this remove added element from dom ? does it unset layouvers[lay] too ?
}
function layoverThis($e){
var p=layover.push($('<div class="overlay" ><span><i class="fa fa-spinner fa-spin"/>loading</span></div>')) - 1;
$e.append(layover[p]);
return p;
}
nearest example i can think of is like opening a folder in windows Os.
when you open a folder: a new window opens to your desktop, while also there is a taskbar tab that is added linked to same window that was appended to your desktop, allowing system to manipulate window (close-restore minimize etc..), so how do i do this in javasript.
my question is how to make such behavior in a manner that is flexible and not hacky or memory wasteful.
You can use a psudoClass and an index - add the event to the delete element as you create the element in the DOM. OR have an onclick that removes the parent div that was appended..
So, you're code will look like:
function layoverThis($e){
var p=layover.push($('<div class="overlay" ><span><i class="fa fa-spinner fa- spin"/>loading</span> <span class="closeThis" onclick="$(this).parent().remove();" > Close </span></div>')) - 1;
$e.append(layover[p]);
return p;
}
Let me know if this works for you, i have a number of other solutions.
Update:
To facilitate the object to be selectable, I would use a psudoClass..
document.ready(function() {
addADiv($('#myParent'));
getArrayOfDivs().css('background-color', 'red');
doAjax(lastClicked);
});
function doAjax(ctrl)
{
}
var lastClicked = "";
$('.dynamicDivs').click(function() {
lastClicked = $(this);
});
function getArrayOfDivs() {
return $('.dynamicDivs');
}
function addADiv($where){
var uniqueIdentifier = $('.dynamicDivs').count() + 1;
$where.append($('<div class="overlay dynamicDivs ' + uniqueIdentifier + ' " ><span><i class="fa fa-spinner fa- spin"/>loading</span></div>'));
}
Related
So I am making some overrides on a Wordpress plugin. I need to copy the event listener on an element and then replace the element and add it back. The event listener is generated by the plugin.
I thought getEventListeners() would work but I have read that it only works in console. If that is this case I'm really astounded. We're in freaking 2020 and I am not finding an obvious solution to this.
What is the solution here people?
Below is the code I was trying to implement having assumed getEventListeners wasn't just a console function.
// Edit Affirm
(function replaceAffirm() {
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const clickHandler = getEventListeners(modalTrigger).click[0].listener;
const substr = learnMore.innerHTML
.toString()
.substring(
learnMore.innerHTML.indexOf("h") + 2,
learnMore.innerHTML.length
);
learnMore.innerHTML = "Easy Financing with " + substr;
modalTrigger.addEventListener("click", clickHandler);
} else {
setTimeout(function () {
replaceAffirm();
}, 250);
}
})();
HTML
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span>
/mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
You can't copy event listeners, but it seems because of the structure of your HTML it's more likely that you shouldn't need to re-add it. Instead of editing the HTML and removing the event listener by doing so, the best bet would be to edit around it.
If you want to remove the text nodes you can iterate through childNodes and separate out what should be removed.
Then to rebuild the appropriate text where you want it you can use insertAdjacentText
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const children = Array.from(learnMore.childNodes);
children.forEach(child => {
if (child.nodeType === Node.TEXT_NODE || child.matches(".affirm-ala-price")) {
if (learnMore.contains(child)) {
learnMore.removeChild(child);
}
}
});
learnMore.insertAdjacentText("afterBegin", "Easy Financing With ");
modalTrigger.insertAdjacentText("beforeBegin", " ");
} else {
setTimeout(function() {
replaceAffirm();
}, 250);
}
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span> /mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
Yes waiting for the Html element to be loaded and checking until it gets loaded is okay and this is one of the correct ways to wait for it.
As per my understanding of your issue, you just have to change the text of the learn-more element.
for that, it is not necessary to copy event listener and then again binding it.
Instead of replacing the whole element just change the text keeping the same element.
So it gets binded with the event listener by default.
I have a button on one page another button on a different page. This second button has the text in it "0 items selected". When the first button is clicked I want to increase the number in the second button by one.
Seeing it is transferring data over different pages I didn't manage to do it the standard way.
Please do not suggest PHP, I am unable to use it.
var yetVisited = localStorage[0];
if ($('.#CallToActionCustomise').click()){
localStorage++;
}
$('.#CallToActionCustomise').click(function(){
$(".Main_MenuButtonReview").append(localStorage);
});
<button class="Main_MenuButtonReview">0 items selected <i class="fa fa-caret-down"></i></button>
You need to access localStorage as if it were an Object, not an array.
Also, you can only store String values, so to get a number to perform math on, you will need to use parseInt.
localStorage.setItem('yetVisited', 0);
$(".Main_MenuButtonReview").text(localStorage.yetVisited + ' items selected');
$('.CallToActionCustomise').click(function()){
localStorage.yetVisited = parseInt(localStorage.yetVisited) + 1;
$(".Main_MenuButtonReview").text(localStorage.yetVisited + ' items selected');
}
Here's an example.
In addition to the above answer, I think you need to init some tag when it loaded.
Put this code on another page.
$(document).ready(function() {
if (typeof localStorage.yetVisited === 'undefined') {
localStorage.setItem('yetVisited', 0);
}
yetVisited = localStorage.yetVisited;
$('.Main_MenuButtonReview').text(yetVisited + ' items selected');
});
$(document).ready(function() {
// this area means after your page(document) ready completely.
});
EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.
Since I want to use classes instead of id's in these functions(I have three of the same function with different things I want to .append) I am sure I need to put $(this) in those functions somewhere to only trigger only ONE function on button click and not all three of them. but I am not sure because I am a total beginner in jquery/js, so I would appreciate some help.
$(document).ready(function () {
$(".onclick").click(function () {
$('#favorites').append('<div data-role="main"class="ui-content"><div class="ui-grid-b"><div class="ui-block-a">Arrow</div><div class="ui-block-b">More Info</div><div class="ui-block-c">Unfavorite</div></div></div>');
});
});
http://codepen.io/anon/pen/JYxqEw - HTML And the Jquery Code
$('.onclick') selects all the elements with a class of onclick. That means that, whenever something with class="onclick" is clicked, that function will fire.
If you want all of those elements to append that exact HTML to the #favorites element, then you can leave your code as-is.
However, if what you're trying to do is append that html to the clicked element, that is when you'd use $(this) -- that selects the element you clicked with jQuery, then you can append directly to that element ie:
$(document).ready(function () {
$(".onclick").click(function () {
// this will append the HTML to the element that triggered the click event.
$(this).append('<div data-role="main"class="ui-content"><div class="ui-grid-b"><div class="ui-block-a">Arrow</div><div class="ui-block-b">More Info</div><div class="ui-block-c">Unfavorite</div></div></div>');
});
});
EDIT
so to insert the contents of each .onclick into #favorites, you'll need to use the innerHTML value of the DOM node. example fiddle:
http://jsbin.com/qazepubuzu/edit?html,js,output
When you select something with jQuery, you're actually getting back not just the DOM node, but a jQuery object -- this object contains both a reference to the actual DOM node ([0]), as well as a jquery object ([1]).
So to select the DOM node with $(this), you target the node: $(this)[0]. Then you can use .innerHTML() to grab the HTML contents of the node and do as you like.
Final result:
$(function () {
$('.onclick').click(function () {
$('#favorites').append( $(this)[0].innerHTML );
});
});
So the building blocks are not that complex, but I think you're a novice jQuery developer and so you may not be clear on the difference between jQuery and JS yet.
$(selector, context) allows us to create a jQuery collection for a CSS selector which is the child of a current context DOM node, though if you do not specify one there is an automatic one (which is document.body, I think). Various functions iterating over jQuery collections make the particular element available as this within the JavaScript. To get to the strong element from the .onclick element in the HTML fragment you need to travel up in the hierarchy, then to the appropriate element. Then, we can collect the text from the element. We can do this in either JS or jQuery.
To do this with simply jQuery:
// AP style title case, because Chicago is too crazy.
var to_title_case = (function () { // variable scope bracket
var lower_case = /\b(?:a|an|the|and|for|in|so|nor|to|at|of|up|but|on|yet|by|or)\b/i,
first_word = /^(\W*)(\w*)/,
last_word = /(\w*)(\W*)$/;
function capitalize(word) {
return word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase();
}
function capitalize_mid(word) {
return lower_case.exec(word) ? word.toLowerCase() : capitalize(word);
}
return function to_title_case(str) {
var prefix = first_word.exec(str),
str_minus_prefix = str.slice(prefix[0].length),
suffix = last_word.exec(str_minus_prefix),
center = str_minus_prefix.slice(0, -suffix[0].length);
return prefix[1] + capitalize(prefix[2]) + center.replace(/\w+/g, capitalize_mid)
+ capitalize(suffix[1]) + suffix[2];
};
})();
$(document).ready(function() {
$(".onclick").click(function () {
var text = $(this).parents('.ui-grid-a').find('.ui-block-a').text();
var html = '<div data-role="main"class="ui-content">'
+ '<div class="ui-grid-b"><div class="ui-block-a">'
+ to_title_case(text) + '</div><div class="ui-block-b">More Info</div>'
+ '<div class="ui-block-c">Unfavorite</div></div></div>';
$("#favorites").append(html);
});
});
I have two IDs in same name ! if any one clicked among them , i need to change the class name of the both IDs. i know we should use ID for single use. Because of my situation(I have two classes for button ) so i have moved to ID.
Her is my code if i click one id that name only changes another one is remains same
<button class="success" id="1" onClick="reply(this.id)"> Added </button>
<button class="success" id="1" onClick="reply(this.id)"> Added </button>
js function
function reply(clicked_id)
{
document.getElementById(clicked_id).setAttribute('class', 'failed');
var el = document.getElementById(clicked_id);
if (el.firstChild.data == "Added")
{
el.firstChild.data = "Add";
}
}
if i use instead of 'class' to id while renaming class which one will be renamed success class or 'class name 1' ?
You can't. Getelementbyid will only return one element. Probably the first one.
Pure JS Is Second Example
My JS Fiddle Example: http://jsfiddle.net/eunzs7rz/
This example will use the class attribute only to perform the switching that you need, its a extremely basic example as do not want to go beyond what is needed... Also i forgot to remove the id's in the JS Fiddle Example.. so just ignore them
THE CSS:
.success {
background-color:#00f;
}
.failed {
background-color:#f00;
}
THE HTML:
<button class="success"> Added </button>
<button class="success"> Added </button>
THE JAVSCRIPT:
$(function() {
$(".success").click(function(){
Reply(this);
});
});
function Reply(oElm) {
$(oElm).attr('class', 'failed');
}
EDIT - PURE JAVASCRIPT VERSION
Sorry, did not think to check the post tags if this was pure JS. But here you go anyway ;)
<style>
.success {
background-color:#00f;
}
.failed {
background-color:#f00;
}
</style>
<button class="success" onclick="Reply(this)"> Added </button>
<button class="success" onclick="Reply(this)"> Added </button>
<script>
function Reply(oElm) {
oElm.className = 'failed';
}
</script>
THE MAIN THING HERE
Once you have the element either by using 'this' or by using 'getElementBy', you can then simply use ".className" to adjust the class attribute of the selected element.
As already explained by others, id is for single use and is quicker than using class or type. So even if you have a group, if only one is ever used.. use an id.
Then you use the object/reference of 'this' from an event on an element, in this case the onclick... that will send that variable to the function / code called.
So using 'this' is a preferred option as it will always reference the element that it is used/called from.
pass elemenet, not it's Id
<button class="success" id="1" onClick="reply(this)"> Added </button>
<button class="success" id="1" onClick="reply(this)"> Added </button>
function reply(elem)
{
$(elem).setAttribute('class', 'failed');
if (elem.firstChild.data == "Added")
{
elem.firstChild.data = "Add";
}
}
the ID attribute must be unique or else it will get the last defined element with that ID.
See this for reference.
Use a class instead of an id. ids are supposed to be unique in a dom tree.
html:
<button class="success" onClick="reply()"> Added </button>
<button class="success" onClick="reply()"> Added </button>
js:
var ary_success = document.querySelectorAll(".success"); // once and forever. If the set of elements changes, move into function `reply`
function reply () {
var elem;
var s_myclasses;
for (var i=0; i < ary_success.length; i++) {
elem = ary_success[i];
s_myclasses = elem.getAttribute('class');
s_myclasses = s_myclasses.replace ( /(success|failed)/g, '' );
s_myclasses = s_myclasses + ' failed';
elem.setAttribute('class', s_myclasses );
if ( elem.firstChild.data.indexOf("Added") !== -1) {
elem.firstChild.data = "Add";
}
}
}
Live Demo here.
Notes
Make sure that you set ary_successin the onload handler or in an appropriately placed script section - at the timeof execution the buttons must be present in the dom ! If in doubt, move it to the start of reply' body.
If you employ jquery, the code simplifies (well...) to:
$(document).ready( function () {
$(".success").on ( 'click', function ( eve ) {
$(".success").removeClass("success").addClass("failed");
$(".success *:first-child:contains('Added')").text(" Add ");
});
});
Updates
Notes, Live Demo
Iterator method changed, every not supported on test platform