Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I've been developing a few functions on JavaScript to display messages on the screen. The function I'm about is this:
showMsg(): Creates an element and appends it into another element.
What I want to do is, given that the function is activated via click, append a timer to this very element and remove it after 3 seconds.
I've tried many ways to accomplish it with setTimeouts and removing the elements within container with listeners like bind.('DOMSubtreeModified') on the container where showMsg() element is being appended to.
I need to give the created element a X timer and remove the element after that X timer ends. Like a sticky bomb, attached to the very element
Can you guys help me out?
Why do you need to have it set to the object in specific? If you want the item to get destroyed after a set time you can simply do this by having saving the object's reference to a variable then having it get destroyed with .remove() inside a setTimeout.
The instance of the setTimeout won't be destroyed so it will act just like having it connected to the object in most use cases.
For example: Here is what I have written that works for what I think you are going for here.
function spawnTemp() {
var deleteMe = $("<div style='height: 100px; width: 100px; background: lime;'>My Message</div>").appendTo("#eleContainer");
setTimeout(function() {
deleteMe.remove();
}, 1000);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<button id="spawnElement" onclick="spawnTemp();">Spawn Temporary</button>
<div id="eleContainer">
</div>
</body>
</html>
Note: in this code I use the appendTo() method to get the instance of the appended object, this allows me to then delete that instance through its reference I stored in the variable deleteMe.
If you were using append() then the stored variable would have been referencing the container that is the parent of the appended object.
If you absolutely need the element to have the timer attached to it, then you can use the more complex,
$("new html").appendTo('container').delay(2000).queue(function() { $(this).remove(); });
Though, it is hard to think of why you might use this over the alternative setTimeout().
Thank you guys, specially Liam for giving me the right approach I needed.
It finally worked out, but in a bit different way.
Calling the function showMsg and storing it into a variable wouldn't allow me to make the timer work, It will create the element and so but it won't remove it.
What I finally did was to create a variable and paste the "result" of the function showMsg(), and then the timer would work. It ended up like this:
THE ANSWER BY THE MOMENT
var elem = $("<div>You must be logged first my friend</div>").appendTo('.notification-bar');
setTimeout( function() {
$(elem).remove();
}, 3000);
And all works well by now.
THE PROBLEM I HAD, EXPLAINED
This is what I was supossed to do:
Functions I got:
createElement: Creates an element with the given attr, data and place
function createElement(element, attributes, position, text) {
var object = $(document.createElement(element)); // Element creation
$.each(attributes, function(key, value) { // Array of attributes
object.attr(key, value); // Attr: Value
});
$(object).appendTo(position); // Place
$(object).append(text); // Text
}
showMsg(): Uses createElement to create and place a div
function showMsg() {
createElement('div', null, '.notification-bar', "You must be logged first my friend");
}
onclick trigger: Called on body load. * This was supossed to work *
$('.reply-button').click(function(event) {
var elem = notifyUser('You must be logged first my friend');
setTimeout( function() {
$(elem).remove();
}, 3000);
});
If anyone has a clue of how to perform the previous problematic, please let me now
Thank you in advance ;)
Related
This question already has an answer here:
Javascript Console Log reporting object properties incorrectly
(1 answer)
Closed 1 year ago.
I'm playing with JavaScript and DOM for a (high school) class I'm teaching. Emphasizing that students use console.log for diagnostics. However, in using it I find the values retrieved from the DOM to be out of sync with the UI.
In the SSCE below, when the LI element is clicked, the first time it should log that the style is "color:red;", but it says style is "text-decoration:line-through;". I added the alert() to give time for anything async to finish but no joy.
If I remove the if( isOn ) clause, the attributes print out as expected, and after the first click attributes also print out as expected.
Guessing there's some synchronizing that needs to happen, just not sure what it may be ...
var theLi = document.getElementById("here");
theLi.addEventListener("click", crossOff);
theLi.setAttribute("style", "color:red;");
theLi.setAttribute("crossed", "off");
function crossOff(element) {
var isOn = this.getAttribute("crossed") == "on";
var attrs = this.attributes;
for (const attr of attrs) {
console.log(attr);
}
alert("huh??");
if (isOn) {
this.setAttribute("style", "text-decoration: none;");
this.setAttribute("crossed", "off");
} else {
this.setAttribute("style", "text-decoration: line-through;");
this.setAttribute("crossed", "on");
}
}
//Write function crossOff here.
<!DOCTYPE html>
<html>
<body>
<h3>To Do List:</h3>
<ul>
<li id=here>Eat</li>
</ul>
<script>
</script>
</body>
</html>
The problem would be that you are logging attribute nodes, which are still linked to the DOM. The console is known to render its output asynchronously, as well as to display DOM elements in a "user-friendly" representation - notice you can even edit them just like in the DOM inspector ("Elements" panel)!
This becomes more clear when you use console.dir instead, and explicitly expand the properties of the object:
In your particular case, don't log the attribute node but rather its contents, like name and value.
I'm JavaScript beginner and am still grasping some concepts around it, so sorry if the question is dumb or it's already answered, but I didn't know what to search for, as the language terminology is still kinda foreign to me (both JavaScript and English). Currently I'm trying to master "this" keyword and trying to minimize JS code inside HTML file and move as much as possible to external files.
This is my question:
Let's say I want to change paragraph's value from Hello World! to foo bar by clicking on paragraph itself, just by using "this" keyword and some JavaScript.
I can do it in external file like:
<!--index.html-->
<p onclick="setParagraphText(this, 'foo bar')">Hello World!</p>
----------------------------------------------
//script.js
function setParagraphText(paragraph, value) {
return paragraph.innerHTML = value;
}
Or inline, inside tag:
<p onclick="this.innerHTML='foo bar'">Hello World!</p>
My question is: is it possible to do a combination of these 2 ways, so that the p value is not passed as an argument, but instead the function is invoked on it as an object (as a similar to 2nd example), but still keep the method of doing it in external file (like in 1st example)?
Something along the lines of this.function(value) instead of function(this, value)
<!--index.html-->
<p onclick="this.setParagraphText('foo bar')">Hello World!</p>
----------------------------------------------
//script.js
function setParagraphText(value) {
//something with innerHTML = value; or whatever will work
}
Thanks in advance!
You can add a data- attribute to your p tag which would store the data you want your text to change to (ie: "foo bar"), and then use addEventListener to add a click event-listener to paragraph tags which have this particular data- attribute. By doing this, you're handing over the javascript logic to the javascript file, and thus limiting the JS written within your HTML file.
See example below:
const clickableElements = document.querySelectorAll('[data-click]');
clickableElements.forEach(elem => {
elem.addEventListener('click', function() {
const value = this.dataset.click;
this.innerHTML = value;
});
});
<p data-click="foo bar">Hello World!</p>
<p data-click="baz">Hello Moon!</p>
First of all, if you want to be able to call your function on this, you have to know what the keyword this here represents. One simple way to do it is to console.log it.
So, this is the DOM element you have your inline javascript on ! Confirmed here: this in an inline event handler. Okay, if you want to have more information, console.log paragraph.constructor.
So, it's a HTMLParagraphElement. That's what you would get if you call this:
document.createElement("p");
So, if you want to be able to call this.setParagraphText, it comes down to calling setParagraphText on an HTMLParagraphElement object. But in order to do that, HTMLParagraphElement has to implement it and one way to do this, as subarachnid suggested, is to add the function to its prototype so that it is shared by all instances of it. If you think this would be useful, take a look at Web Components.
Here is a link: Extending native HTML elements.
Basically, you do it like this (and the cool thing here is the functionality, that is changing its content when clicking on it, will be encapsulated within the class):
<!DOCTYPE html>
<html>
<body>
<script>
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class CoolParagraph extends HTMLParagraphElement {
constructor() {
super();
this.addEventListener('click', e => this.setParagraphText('new value'));
}
setParagraphText(v) {
this.innerHTML = v;
}
}
customElements.define('cool-paragraph', CoolParagraph, {extends: 'p'});
</script>
<!-- This <p> is a cool paragraph. -->
<p is="cool-paragraph">Cool paragraph! Click on me and the content will change!</p>
</body>
</html>
So you don't even have to write inline Javascript anymore!
But if you want to have it your way and add your inline javascript, it's fine.
<!-- Note the this.setParagraphText(), now it works! -->
<p is="cool-paragraph" onclick="this.setParagraphText('foo bar')">Cool paragraph! Click on me and the content will change!</p>
<!DOCTYPE html>
<html>
<body>
<script>
// See https://html.spec.whatwg.org/multipage/indices.html#element-interfaces
// for the list of other DOM interfaces.
class CoolParagraph extends HTMLParagraphElement {
constructor() {
super();
//this.addEventListener('click', e => this.setParagraphText('new value'));
}
setParagraphText(v) {
this.innerHTML = v;
}
}
customElements.define('cool-paragraph', CoolParagraph, {extends: 'p'});
</script>
<!-- This <p> is a cool paragraph. -->
<p is="cool-paragraph" onclick="this.setParagraphText('foo bar')">Cool paragraph! Click on me and the content will change!</p>
</body>
</html>
I don't know if that answers your question, but that should hopefully points you in the right direction.
For your code to work you would have to enhance the HTMLElement prototype with your setParagraphText method (which is basically just a wrapper for this.innerHTML = value):
HTMLElement.prototype.setParagraphText = function(value) {
this.innerHTML = value;
};
Now something like this should work:
<p onclick="this.setParagraphText('foo bar')">Hello World!</p>
But I would strongly advise against modifiying native prototypes (older browsers like IE 9 don't even allow such a thing afaik).
Currently I'm trying to [...] minimize JS code inside HTML file and move as much as possible to external files.
Then how about having no JS code inside your HTML? This lets you kill two birds with one stone:
document.getElementById("clickme").addEventListener("click", function() {
this.innerHTML = "foo bar"
})
<p id="clickme">Hello world!</p>
The listener you add with addEventListener will be invoked with this being the element that the listener was added on.
Dunno if this is what you're after, but this in the function is referring to the function itself, but you can send in a scope with .call(this, arguments) which means that referring to this in the function is actually the scope of the HTML element.
I'm showing two ways of how you can handle this, with either a data attribute or sending in a new value as a parameter.
function setParagraphText(newValue) {
this.innerText = this.dataset.text + " + " + newValue;
}
<p data-text="foo bar" onclick="setParagraphText.call(this, 'foo yah')">Hello World!</p>
Also read: Javascript call() & apply() vs bind()?
I'm working on something where multiple functions will add various Event listeners to an initially hidden div, let's just call it secretBlock. Only one will ever be active at any given point, but all said functions will manipulate it by:
First cloning sercetBlock to ensure no previous listeners are still attached
Then setting the display to flex
HTML:
<div id="secretBlock" hidden>Secret</div>
JavaScript:
function exampleFuction() {
var secretBlock = document.getElementById('secretBlock');
var secretClone = secretBlock.cloneNode(true);
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock.style.display = 'flex';
....
}
but the last part, setting the display, is not firing.
I assumed this had something to do with async-ness, but
setTimeout(function(){ secretBlock.style.display = 'flex' }, 999);
also had no effect.
However, one of the functions appends the div inside of another div right after setting the display, causing it to fire properly:
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock.style.display = 'flex';
otherDiv.appendChild(secretBlock);
After a bit of testing, I found out it doesn't matter when I set the display (now vs later) or where it is in the code, as long as secretBlock gets appended to another div, the display change will register, otherwise staying hidden.
.......which sorta left me clueless as to what's going on, any insight would thus be much appreciated~~
Was a reference issue.
After .replaceChild() replaces secretBlock, the initial reference:
var secretBlock = document.getElementById('secretBlock')
becomes obsolete as it still points to the old, original element which is not apart of the html document anymore. Thus you need to redirect the reference to the cloned element:
secretBlock.parentNode.replaceChild(secretClone, secretBlock);
secretBlock = document.getElementById('secretBlock');
secretBlock.style.display = 'flex';
Thanks Dr.Molle!
I have on a page a tag links generated from a database with an id. What im trying to do is in an alert box display the text inside the a tag.
Ive tried to have a look to see if I can see a previous question, which I have come up with the following but all I get in the alert box is object HTMLCollection
I have the following code:
<a id="bet" onclick="addSlip();" class="btn btn-round" style="text-align: left;">'.$home->one.' <span>'.$home->two.'</span></a>
and...
function addSlip() {
document.getElementById('bet').innerHTML=bet;
alert(bet);
}
Thanks for any constructive answers
You should do the following
function addSlip() {
var bet = document.getElementById('bet').textContent;
alert(bet);
}
the rest as it is.
or using jquery
function addSlip() {
var bet = $("#bet").text()
alert(bet);
}
The main problem of your program was that the alerted variable had no value. (undefined)
The way you wrote it if the variable bet had a value would change the innerHTML of the of the a tag to that value.
Now to the using innerHTML or textContent part. As mentioned here in terms of performance the textContent is better
You need to reverse the variable assignment of bet:
function addSlip() {
var bet = $("#bet").text();// or $("#bet").html() if you want the HTML alerted
alert(bet);
}
In your case alert will give you the HTML Object Collection only.
When you have an element with id, then you can access them anywhere in javascript
e.g. for the following HTML
<div id="bet">This is a div </div>
in JS
alert(bet);
will give you the html object collection.
Solution for you is update your code
function addSlip() {
document.getElementById('bet').innerHTML=bet;
alert(bet);
}
to
function addSlip() {
alert( document.getElementById('bet').innerHTML);
}
If there is some html elements also in the div you can update the function with
function addSlip() {
alert(document.getElementById('bet').textContent);
}
I have have multiple divs' with similar code within it, but also has a unique id within the main div that calls a toggleClass & slideToggle function. I'm trying to create a loop so that I do not have to write similar code for each element.
--- working code --- (where numeric value after the id would change)
$('#er1').click(function() {
$('#er1').toggleClass('but-extra-on');
$('#cr1').toggleClass('but-closerow-on');
$('#er1').next('div').slideToggle('slow');
return false;
});
-- not working code -- (I want to have functions for the click of #er1, #er2, #er3 etc.)
var count = 1;
while (count < 10){
var curER = 'er'+count;
var curCR = 'cr'+count;
$('#'+curER).click(function() {
$('#'+curER).toggleClass('but-extra-on');
$('#'+curCR).toggleClass('but-closerow-on');
$('#'+curER).next('div').slideToggle('slow');
});
count++;
}
* for some reason, when I use the while code, whenever I click #er1, #er2, #er3 etc.. only the event for #er9 toggles.
You can solve this problem, by using the $(this) selector for the one that you are clicking, and attaching an html data attribute to the div, specifying the other div that you want to change when you click it and selecting the other one with that... make sense.. probably not? Check out Solution 1 below.
The second solution is to use jQuery Event Data to pass the count variable into the event listener.
Solution 1: http://jsfiddle.net/Es4QW/8/ (this bloats your html a bit)
Solution 2: http://jsfiddle.net/CoryDanielson/Es4QW/23/
I believe the second solution is slightly more efficient, because you have slightly less HTML code, and the $(this) object is slightly smaller. Creating the map and passing it as event data, I believe, is less intensive... but... realistically... there's no difference... the second solution has cleaner HTML code, so you should use that.
Solution 3 w/ .slideToggle(): http://jsfiddle.net/CoryDanielson/Es4QW/24/
Edit: Updated solutions by passing in the selected elements instead of the selectors. Now each click event will not do a DOM lookup as it did before.
I've run into this problem before. I fixed it by extracting the event listener code into its own function like so:
for (var i = 1; i < 10; i++) {
attachClickEvent('er'+i, 'cr'+i);
)
function attachClickEvent(cr, er)
{
$('#'+er).click(function() {
$(this).toggleClass('but-extra-on');
$('#'+cr).toggleClass('but-closerow-on');
$(this).next('div').slideToggle('slow');
});
}