I am using jQuery to append elements to a div, and all works fine.
var new_div = $('<div>My stuff</div>');
new_div.appendTo("#container");
However, I'd like the div to appear by fading in, instead of abruptly.
I notice though that I get an error when I try to access graphic properties on my dynamically generated element. So this, for example fails:
new_div.hide().fadeIn();
The console reports the following error:
TypeError: jQuery.curCSS is not a function
Do I understand this correctly, that this fails because current css properties are not defined for the dynamically generated element? Or what else can be goingg wrong?
Important edit
Additional checking and working on this pointed out to a complete misunderstanding from my part. This has nothing to do with the fact that the element was dynamically generated. I got the same thing by calling fadeIn() on whatever element.
I sincerely apologize!
I still didn't get, though, why this happens
Adding elements to the DOM takes some time, miliseconds maybe, but it's still a reason for jquery not be able to find the element.
This process might be even slower if the DOM is a large html page.
Write your code like this:
var new_div = $('<div>My stuff</div>');
new_div.appendTo("#container");
setTimeout( function(){
new_div.hide().fadeIn();
} , 150); // 100 could be also good
It might be enough time for jquery to catch the element.
I would add an id to keep track of all elements I'm creating (just my preference, but it makes it easier to code it).
var new_div = '<div id="myNewDiv1" style="display:none;">My Styff</div>'
$('body').append(new_div);
$('#myNewDiv1').fadeIn();
It does seem to be a compatibility question, although I wasn't able to figure out exactly why and how to fix it.
Adding this code fixes the problem though:
jQuery.curCSS = function(element, prop, val) {
return jQuery(element).css(prop, val);
};
Related
I'm using two simple addEventListener mouseenter and mouseleave functions respectively to play and stop animations (Bodymovin/SVG animations, though I suspect that fact is irrelevant).
So, the following works fine:
document.getElementById('animationDiv').addEventListener('mouseenter', function(){
animation.play();
})
(The HTML couldn't be simpler: The relevant part is just an empty div placeholder filled by script - i.e., <div id="animationDiv"></div>.
I can place that in the same file as the one that operationalizes the animation code, or I can place it in a separate "trigger" file, with both files (and other others necessary to processing) loaded in the site footer.
The problem arises when I need to be able to set triggers for any of multiple similar animations that may or may not appear on a given page.
If only one of two animatable elements are present on a page, then one of two sets of triggers will throw an error. If the first of two such triggers is not present, then the second one will not be processed, meaning that the animation will fail. Or at least that's what it looks like to me is happening.
So, just to be clear, if I add the following two triggers for the same page, and the first of the following two elements is present, then the animation will play on mouseenter. If only the second is present, its animation won't be triggered, apparently because of the error thrown on the first.
document.getElementById('firstAnimationDiv').addEventListener('mouseenter', function(){
firstAnimation.play();
})
document.getElementById('secondAnimationDiv').addEventListener('mouseenter', function(){
secondAnimation.play();
})
At present I can work around the problem by creating multiple trigger files, one for each animation, and setting them to load only when I know that the animatable element will be present, but this approach would get increasingly inefficient when I am using multiple animations per page, on pages whose content may be altered.
I've looked at try/catch approaches and also at event delegation approaches, but so far they seem a bit complicated for handling this simple problem, if appropriate at all.
Is there an efficient and flexible standard method for preventing or properly handling an error for an element not found, in such a way that subsequent functions can still be processed? Or am I missing something else or somehow misreading the error and the function failure I've been encountering?
WHY I PICKED THE ANSWER THAT I DID (PLUS WORKING CODE)
I was easily able to make the simple, directly responsive answer by Baoo work.
I was unable to make the answers below by Patrick Roberts and Crazy Train work, though no doubt my undeveloped js skills are entirely at fault. When I have the time, or when the issue next comes up for me in a more complex implementation (possibly soon!), I'll take another look at their solutions, and see if I can either make them work or if I can formulate a better question with fully fledged coding examples to be worked through.
Finally, just to make things clear for people who might be looking for an answer on Bodymovin animations, and whose js is even weaker than mine, the following is working code, all added to the same single file in which a larger set of Bodymovin animations are constructed, relieving me of any need to create separate trigger files, and preventing TypeErrors and impaired functionality.
//There are three "lets_talk" animations that can play - "home," "snug," and "fixed"
//and three types of buttons needing enter and leave play and stop triggers
let home = document.getElementById('myBtn_bm_home');
if (home) home.addEventListener('mouseenter', function() {
lets_talk_home.play();
});
if (home) home.addEventListener('mouseleave', function() {
lets_talk_home.stop();
});
let snug = document.getElementById('myBtn_bm_snug');
if (snug) snug.addEventListener('mouseenter', function() {
lets_talk_snug.play();
});
if (snug) snug.addEventListener('mouseleave', function() {
lets_talk_snug.stop();
});
let fixed = document.getElementById('myBtn_bm_fixed');
if (fixed) fixed.addEventListener('mouseenter', function() {
lets_talk_fixed.play();
});
if (fixed) fixed.addEventListener('mouseleave', function() {
lets_talk_fixed.stop();
});
At typical piece of underlying HTML (it's generated by a PHP function taking into account other conditions, so not identical for each button), looks like this at the moment - although I'll be paring away the data-attribute and class, since I'm not currently using either. I provide it on the off-chance that someone sees something significant or useful there.
<div id="letsTalk" class="lets-talk">
<a id="myBtn" href="#"><!-- a default-prevented link to a pop-up modal -->
<div class="bm-button" id="myBtn_bm_snug" data-animation="snug"></div><!-- "snug" (vs "fixed" or "home" is in both instances added by PHP -->
</a>
</div>
Obviously, a more parsimonious and flexible answer could be - and probably should be - written. On that note, correctly combining both the play and stop listeners within a single conditional would be an obvious first step, but I'm too much of a js plodder even to get that right on a first or second try. Maybe later/next time!
Thanks again to everyone who provided an answer. I won't ask you to try to squeeze the working solution into your suggested framework - but I won't ask you not to either...
Just write your code so that it won't throw an error if the element isn't present, by simply checking if the element exists.
let first = document.getElementById('firstAnimationDiv');
if (first) first.addEventListener('mouseenter', function() {firstAnimation.play();});
You could approach this slightly differently using delegated event handling. mouseover, unlike mouseenter, bubbles to its ancestor elements, so you could add a single event listener to an ancestor element where every #animationDiv is contained, and switch on event.target.id to call the correct play() method:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
switch (event.target.id) {
case 'firstAnimationDiv':
return firstAnimation.play();
case 'secondAnimationDiv':
return secondAnimation.play();
// and so on
}
});
You could also avoid using id and use a more semantically correct attribute like data-animation as a compromise between this approach and #CrazyTrain's:
document.getElementById('animationDivContainer').addEventListener('mouseover', function (event) {
// assuming <div data-animation="...">
// instead of <div id="...">
switch (event.target.dataset.animation) {
case 'first':
return firstAnimation.play();
case 'second':
return secondAnimation.play();
// and so on
}
});
First, refactor your HTML to add a common class to all of the placeholder divs instead of using unique IDs. Also add a data-animation attribute to reference the desired animation.
<div class="animation" data-animation="first"></div>
<div class="animation" data-animation="second"></div>
The data- attribute should have a value that targets the appropriate animation.
(As #PatrickRobers noted, the DOM selection can be based on the data-animation attribute, so the class isn't really needed.)
Since your animations are held as global variables, you can use the value of data-animation to look up that variable. However, it would be better if they weren't global, but were rather in a common object.
const animations = {
first: null, // your first animation
second: null, // your second animation
};
Then select the placeholder elements by class, and use the data attribute to see if the animation exists, and if so, play it.
const divs = document.querySelectorAll("div.animation");
divs.forEach(div => {
const anim = animations[div.dataset.animation];
if (anim) {
anim.play(); // Found the animation for this div, so play it
}
});
This way you're guaranteed only to work with placeholder divs that exist and animations that exist.
(And as noted above, selection using the data attribute can be done const divs = document.querySelectorAll("div[data-animation]"); so the class becomes unnecessary.)
Can some one show how I can change the InnerHTML of the titles class to be the same as the alt attribute. For the actual website jarretonions.co.za
Thanks
$(document).ready(function() {
$(".pic").on("click", function() {
$(".modal").show();
var srclong = $(this).attr("src");
var srcshort = srclong.split("_");
var srcextension= srclong.split(".");
$(".modal img").attr("src", srcshort[0]+'.'+srcextension[1]);
************is it something like this********
var title = $(this).attr("alt");
$(".modal span").InnerHTML= title;
OR
document.getElementByClassName('titles').innerHTML = title;
})
+
echo
"<div class='art'>
<img class='pic' src='img/".$row["name"]."_tnail.jpg'
alt='".$row["name"]." • ".$row["year"]." • ".$row["type"]."'
height='auto' width='100%'/>
<div class='modal'>
<img class='big'/>
<span class='titles'></span>
</div>
</div>"
;
Since you're using JQuery, you can select those elements using $(".title") and change them directly. Something like so:
$(document).ready(function() {
$(".pic").on("click", function() {
$(".title").text( $(this).attr("alt") );
})});
Here's a fiddle: https://jsfiddle.net/wmjtfLja/1/
Note that if you have more than one element of class .title, they will all change. So you may want to select the title element by id or by relative path from the clicked image.
Realizing in advance, the danger of providing an answer that is not (superficially) fully aligned with the question, I was struck by the comment from melpomene, whom I initially thought was refering to things not existing in jquery.
melpomene is 100% correct, since getElementByClassName does not exist.
The correct syntax is getElementsByClassName.
Having said that, helloworld is also correct (syntax errors aside), since loading jquery for every little task is really redundent, and one can manipulate by class with little more half a dozen lines of pure javascript.
But, getting elements by class has dangers, since the return is a 'live' array.
For example, with dylan's original question, getting by class is only useful to return the first instance (the array length is just a guide of how many elemnts it applies to). Therefore, for dylan to make changes as he proposed, each requires its own button. (which also means, michael that I believe you are incorrect when you say it will affect all elements with same class name - oth, you are fully correct in noting that one should inpsect for other values (or change the class name) when running loops on the attribute).
Consider the following (on the fly class change);
function otf_cls_change(cls_original,cls_replace){
var a=document.getElementsByClassName(cls_original);
l=a.length;
if (l==0){return 0;}
do {
a[0].setAttribute('class',cls_replace);
a=document.getElementsByClassName(cls_original);
l=a.length;
} while (l>0);
}
This is effective for changing class names on the fly.
But, if we modify the code and
//change this a[0].setAttribute('class',cls_replace); // to
a[0].innerHTML='this_html';
It will cause the browser to hit an endless loop.
Why? because the live array returned by ElementByClass will only process the first item (even if you try to loop the array).
Therefore, while changing the class on the fly is fun and very do-able, I'd strongly suggest that using it to change any attrib that is not specific to the class id is a bad idea.
Changing the class attrib in conjunction with another attrib is fine.
For example,
a[0].innerHTML='this_html'; //do this first
a[0].setAttribute('class',cls_replace); //then this
The above will work to loop class defined elements.
On a point of massive personal hypocrisy, I do get annoyed when people ask for pure javascript solutions, and then some wing nut chimes in with jquery. I guess I'm doing the opposite here, since evidently, the question was jquery related, and here I am throwing out pure javascript. Sorry bout that.
btw, dylan, good luck with it. glad you bit back on the negative comment. Too many people here are terrified of offending, and wind up get bullied.
hth,
Gary
I am working on some stuff meanwhile i get into something which i didn't understand core reason behind it.
my idea(good or bad) is to clone a document and add changes to it and then reassign my cloned object into document
var tu=document.clone(true);
getComputedStyles(document)//returns all current css values
tu.getElementsByTagName("body")[0].style.backgroundColor="yellow";//when i print tu and inspect style is visible on body
tu.getElementsByTagName("body")[0].style.width="100px";
getComputedStyles(tu)//returns all empty values.
document=tu;
But every step return without an error but after assigning tu to document my body bgColor is still white.
one more thing why getComputedStyles() are returning empty values?
Finally i got something which might be usefull
var documentClone=document.clone(true);
documentClone.body.style.background="yellow";
documentClone.body.style.background="red";`\\many number of operations`
//after many changes
document.replaceChild(documentClone.documentElement,document.documentElement);
//now you can find all your changes..
window.document is not writeable.
I have a pretty specific scenario where I would like to select all elements with jQuery, make a CSS change, save the elements, then reverse the change I made.
The Goal
I created a jQuery plugin called jQuery.sendFeedback. This plugin allows the user to highlight areas of the screen, as shown in this demo. When they submit their feedback the plugin grabs all the HTML on the page and dumps it into a callback function. Like so:
$('*').each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
var feedbackInformation = {
subject: $feedbackSubject.val(),
details: $feedbackDetails.val(),
html: '<html>' + $('html').html() + '</html>'
};
if (settings.feedbackSent)
settings.feedbackSent(feedbackInformation);
The callback function accepts this feedback information and makes an AJAX call to store the page HTML on the server (this HTML includes the red box highlights the user drew on the screen). When someone from tech support needs to view the user's "screen shot" they navigate to a page that serves up the stored HTML so the developer can see where the user drew their highlights on the screen.
My original problem was that different screen resolutions made the elements different sizes and the red highlights would highlight the wrong areas as the screen changed. This was fixed pretty easily by selecting all elements on the page and manually setting their height and width to their current height and width when the user takes the snap shot. This makes all the element sizes static, which is perfect.
$('*').each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
The Problem
The issue with this is that when the plugin is done transmitting this HTML the page currently being viewed now has static heights and widths on every element. This prevents dropdown menus and some other things from operating as they should. I cannot think of an easy way to reverse the change I made to the DOM without refreshing the page (which may very well end up being my only option). I'd prefer not to refresh the page.
Attempted Solution
What I need is a way to manipulate the HTML that I'm sending to the server, but not the DOM. I tried to change the above code to pull out the HTML first, then do the operation on the string containing the HTML (thus not affecting the DOM), but I'm not quite sure what I'm doing here.
var html = '<html>' + $('html').html() + '</html>';
$('*', html).each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
This did not work. So either I need to be able to manipulate the string of HTML or I need to be able to manipulate the DOM and undo the manipulation afterward. I'm not quite sure what to do here.
Update
I employed the solution that I posted below it is working beautifully now. Now I am wondering if there is a way to statically write all the css for each element to the element, eliminating the need for style sheets to be referenced.
I think you are mostly on the right track by trying to make the modifications to the HTML as a string rather than on the current page for the user.
If you check this post, you might also want to follow the recommendation of creating a temporary <div> on the page, cloning your intended content to the new <div> ensuring it is invisible using "display:none." By also putting a custom Id on the new <div> you can safely apply your static sizing CSS to those elements using more careful selectors. Once you have sent the content to the server, you can blow away the new <div> completely.
Maybe?
After much pain and suffering I figured a crude but effective method for reverting my modifications to the DOM. Though I hadn't gotten around to trying #fdfrye's suggestion of cloning, I will be trying that next to see if there is a mroe elegant solution. In the meantime, here is the new code in case anyone else can benefit from it:
$('*').each(function () {
if ($(this).attr('style'))
$(this).data('oldStyle', $(this).attr('style'));
else
$(this).data('oldStyle', 'none');
$(this).width($(this).width());
$(this).height($(this).height());
});
var html = '<html>' + $('html').html() + '</html>';
$('*').each(function () {
if ($(this).data('oldStyle') != 'none')
$(this).attr('style', $(this).data('oldStyle'));
else
$(this).removeAttr('style');
});
When I'm looping through every element and modifying the css, I log the original value onto the element as data. After I assign the DOM HTML to a variable I then loop through all elements again and restore the style attribute to its original value. If there was no style attribute then I log 'none' to the element data and then remove the style attribute entirely when looping through again.
This is more performance heavy than I wish it was since it loops through all elements twice; it takes a few seconds to finish. Not horrible but it seems like a little much for such a small task. Anyway, it works. I get a string with fixed-sized HTML elements and the DOM goes back to normal as if the plugin never touched it.
I have code to create another "row" (div with inputs) on a button click. I am creating new input elements and everything works fine, however, I can't find a way to access these new elements.
Example: I have input element (name_1 below). Then I create another input element (name_2 below), by using the javascript's createElement function.
<input type='text' id='name_1' name="name_1" />
<input type='text' id='name_2' name="name_2" />
Again, I create the element fine, but I want to be able to access the value of name_2 after it has been created and modified by the user. Example: document.getElementById('name_2');
This doesn't work. How do I make the DOM recognize the new element? Is it possible?
My code sample (utilizing jQuery):
function addName(){
var parentDiv = document.createElement("div");
$(parentDiv).attr( "id", "lp_" + id );
var col1 = document.createElement("div");
var input1 = $( 'input[name="lp_name_1"]').clone(true);
$(input1).attr( "name", "lp_name_" + id );
$(col1).attr( "class", "span-4" );
$(col1).append( input1 );
$(parentDiv).append( col1 );
$('#main_div').append(parentDiv);
}
I have used both jQuery and JavaScript selectors. Example: $('#lp_2').html() returns null. So does document.getElementById('lp_2');
You have to create the element AND add it to the DOM using functions such as appendChild. See here for details.
My guess is that you called createElement() but never added it to your DOM hierarchy.
If it's properly added to the dom tree you will be able to query it with document.getElementById. However browser bugs may cause troubles, so use a JavaScript toolkit like jQuery that works around browser bugs.
var input1 = $( 'input[name="lp_name_1"]').clone(true);
The code you have posted does not indicate any element with that name attribute. Immediately before this part, you create an element with an id attribute that is similar, but you would use $("#lp_1") to select that, and even that will fail to work until you insert it into the document, which you do not do until afterwards.
var input1 = $( 'input[name="lp_name_1"]').clone(true);
should be
var input1 = $( 'input[#name="lp_name_1"]').clone(true);
Try that first, check that input1 actually returns something (maybe a debug statement of a sort), to make sure that's not the problem.
Edit: just been told that this is only true for older versions of JQuery, so please disregard my advice.
Thank you so much for your answers. After walking away and coming back to my code, I noticed that I had made a mistake. I had two functions which added the line in different ways. I was "100% sure" that I was calling the right one (the code example I posted), but alas, I was not.
For those also experiencing problems, I would say all the answers I received are a great start and I had used them for debugging, they will ensure the correctness of your code.
My code example was 100% correct for what I was needing, I just needed to call it. (Duh!)
Thanks again for all your help,
-Jamie