Reset html link attribute - javascript

I have the following line in my html
a link
Then I'm using the chrome dev console to change the attribute data-url to another link. Can I in some way afterwards reset this link to its default value? I've seen a reset() function but I guess it doesn't work for this problem.

Store in another attribute
const anc = document.getElementById("anc1");
anc.dataset.saveurl = anc.dataset.url;
anc.dataset.url="otherurl";
Click
result:
Click

Another perfect use case for WeakMaps! 🎉🎉🎉
const wm = new WeakMap()
document.querySelectorAll('button').forEach(el => {
wm.set(el, el.dataset.url)
delete el.dataset.url
el.onclick = () => {
console.log('html:', el.outerHTML, '\nweakmap:', wm.get(el))
}
})
<button data-url="img/1.jpg">Click</button>
<br>
<button data-url="img/2.jpg">Click</button>
<br>
<button data-url="img/3.jpg">Click</button>

Related

Unable to add Event Listener on imported Element Node

I am fetching a list of posts from an API and displaying them on webpage. Now, there is a Delete button associated with each post which when clicked should remove the post.
index.html
<template id="single-post">
<li class="post-item">
<h2></h2>
<p></p>
<button>DELETE</button>
</li>
</template>
<ul class="posts"></ul>
app.js
const listElement = document.querySelector('.posts');
const postTemplate = document.getElementById('single-post');
const listOfPosts = await sendHttpRequest(
'GET',
'https://jsonplaceholder.typicode.com/posts'
);
// listOfPosts is already in parsed format
for (const post of listOfPosts) {
const postEl = document.importNode(postTemplate.content, true);
postEl.querySelector('h2').textContent = post.title.toUpperCase();
postEl.querySelector('p').textContent = post.body;
listElement.append(postEl);
const btn = postEl.querySelector('button');
console.log(btn, postEl);
btn.addEventListener('click', () => {
postEl.remove();
});
}
The above code only fetches first post only and throws
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'addEventListener')
at HTMLButtonElement.fetchPosts
When I remove the Event Listener, the code works fine.
I guess this is something to do with importNode method since I have done similar things with createElement and they worked fine
EDIT
I did some little experimenting. The JSON post object returned by API also consisted of an id field. So, I basically added that id to each button that was being created.
Another thing is I used event delegation to remove() the li whose button is clicked.
And very surprisingly It works
const listElement = document.querySelector('.posts');
const postTemplate = document.getElementById('single-post');
const listOfPosts = await sendHttpRequest(
'GET',
'https://jsonplaceholder.typicode.com/posts'
);
// listOfPosts is already in parsed format
for (const post of listOfPosts) {
const postEl = document.importNode(postTemplate.content, true);
postEl.querySelector('h2').textContent = post.title.toUpperCase();
postEl.querySelector('p').textContent = post.body;
postEl.querySelector('button').id = post.id; // HERE
listElement.append(postEl);
}
// delete the li element
listElement.addEventListener('click', (event) => {
if(event.target.tagName === 'BUTTON') {
console.log(event.target);
event.target.parentElement.remove();
}
})
when clicked on first list post's DELETE button, it consoles
<button id="1">DELETE</button>
and removes that item.
This bascially proves that the button tag is certainly there since we are able to query select and set its id.
Strangely, when consoled it shows null.
Your code errors out here:
btn.addEventListener('click', () => {
postEl.remove();
});
The error message clarifies that btn is null, which means that postEl.querySelector('button') returned null, which means that there is no button tag inside postEl.
You will need to carefully look at the result of document.importNode(postTemplate.content, true) and see what it contains. You will see that it does not contain a button tag. So, either the button was not added, in which case you will need to adjust importNode, or, the button is not a button tag, but something else, like <input type="button" value="foo"> for example.

DOM change font color by using addEventListener

I am a beginner of JS and HTML.
I encounter a problem with changing the font color by using addEventListener
here is my part of HTML code
<form>
<input type = 'color' class = 'color'>
</form>
here is my js code.
// this is for class color
const color = document.querySelectorAll('.color');
// I have 2 div block which contains messages.
const showTextBox = document.querySelectorAll('.mtext1');
// for each message, they correspond to the different color boxes.
color.forEach((element, index) => {
element.addEventListener('click', function (e) {
console.log(e)
showTextBox[index].style.color = color[index].value;
})
});
the situation I encounter is when I click the color box, it pops up the Palette, and then you have to "click" again for choosing the color. However, that addEventListener only available for the first click.
What I thought is maybe I can use nested addEventListener?
or does javascript has a more efficient way to change color dynamically(or responsively) to solve double click situation?
you can try this solution. Use input event instead of click.
const color = document.querySelectorAll('.color');
const showTextBox = document.querySelectorAll('.mtext1');
color.forEach((element, index) => {
element.addEventListener('input', function (e) {
showTextBox[index].style.color = element.value;
})
});

page back button not resetting css

I have a button that "navigates" to a new page.
To do this the button makes the current section's/page's display settings = none and the new section/page's display = block.
<div class="listLeft"><p class="listItems" onclick="navButtons('work')">work</p><p class="listItems" onclick="navButtons('about')">about</p></div>
<div class="listRight"><p class="listItems" onclick="navButtons('playground')">playground</p><p class="listItems" onclick="navButtons('contact')">contact</p></div>
js below
function navButtons(page) {
let section = document.getElementById(page);
section.setAttribute("class", "visible");
window.location.href = `#${page}page`;
window.scrollTo(0, document.body.scrollHeight);
let pagesArrayCopy = [...pagesArray]
const unselected = pagesArrayCopy.filter(item => item.id !== page);
setTimeout(() => {
unselected.forEach((el, index) => unselected[index].setAttribute("class", "invisible"));
pagesArray = Array.from(pagesNodeList);
}, 500);
};
when I press the back button the url is obviously changing back to the old #section, but the css doesn't reset, so the new page is still visible and the previous page is still display = none.
How do i get around this, so when the back button is pressed the css values reset to default?
Try to use an anchor tag so your URL actually change (ie: update the adress to mypage.html#page2).
Then you could bind a function to onhashchange and check of your page is well displayed.
you haven't supplied all the html. there's no id work so section is null
function navButtons(page) {
let section = document.getElementById(page);
console.log(section)
section.setAttribute("class", "visible");
window.location.href = `#${page}page`;
window.scrollTo(0, document.body.scrollHeight);
let pagesArrayCopy = [...pagesArray]
const unselected = pagesArrayCopy.filter(item => item.id !== page);
setTimeout(() => {
unselected.forEach((el, index) => unselected[index].setAttribute("class", "invisible"));
pagesArray = Array.from(pagesNodeList);
}, 500);
};
<div class="listLeft">
<p class="listItems" onclick="navButtons('work')">work</p>
<p class="listItems" onclick="navButtons('about')">about</p></div>
<div class="listRight">
<p class="listItems" onclick="navButtons('playground')">playground</p>
<p class="listItems" onclick="navButtons('contact')">contact</p></div>
Another way to do this is to bind your changing state to the history of the browser. Since there is no real way of overriding the 'back' button on the browser, you will have to write it into your history. See the History API for HTML5 here:
https://css-tricks.com/using-the-html5-history-api/
The same thing specified above can also be done with this plugin:
https://code.google.com/archive/p/reallysimplehistory/

How to single select button using jquery?

How can I manipulate CSS and read text value and save it into variable when clicking on the button. when I click on the button using jquery how can I add a CSS to the button such as class="btn-n-active".
How to make sure that only one button is selected while switching on different buttons and that button should have active CSS
I was trying to just read the single value from the options, just a single selection.
<p>
<button onclick="myFunction()" class="btn-n">DOG</button>
<button onclick="myFunction()" class="btn-n">CAT</button>
<button onclick="myFunction()" class="btn-n">LION</button>
<button onclick="myFunction()" class="btn-n">PIG</button>
</p>
const myFunction = () => {
}
It's like a quiz system where I just want to read a single value. I am not able to apply the logic
There are a bunch of options to do this. Below you will see 3 of them.
The idea is to select all buttons and remove the active class and then add it to the button you clicked on.
My jquery is a bit rusty but I suggest you use just javaScript for such a simple request
const myFunction = (event) => {
const clickedElem = event.target
const allBtns = document.querySelectorAll('.btn-n')
allBtns.forEach(btn => btn.classList.remove("btn-n-active"))
clickedElem.classList.add("btn-n-active")
}
// option 2 without adding function in html
/* const allBtns = document.querySelectorAll('.btn-n')
allBtns.forEach(btn => btn.onclick = () => {
allBtns.forEach(btn => btn.classList.remove("btn-n-active"))
btn.classList.add("btn-n-active")
}) */
//option3 simple jQuery
/* const allBtns = $('.btn-n');
allBtns.click(function() {
$(this).addClass("btn-n-active")
allBtns.not(this).removeClass("btn-n-active")
}) */
const submit = () => {
const selectedText = document.querySelector(".btn-n-active") ? document.querySelector(".btn-n-active").innerText : 'Please select one'
console.log(selectedText)
}
.btn-n-active {
background: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>
<button onclick="myFunction(event)" class="btn-n">DOG</button>
<button onclick="myFunction(event)" class="btn-n">CAT</button>
<button onclick="myFunction(event)" class="btn-n">LION</button>
<button onclick="myFunction(event)" class="btn-n">PIG</button>
</p>
<button onclick="submit()">Click to see selected text</button>
You can find the selected answer button having the active class using the hasClass('btn-n-active')
$("button").click(function(){
$("button").removeClass("btn-n-active"); // Remove other active class
$(this).addClass("btn-n-active"); // Add active class to the clicked button
});
const myFunction = () => {
var selectedButtonText = $("button").hasClass("btn-n-active").html();
console.log("Selected answer: " + selectedButtonText);
}

IOS show keyboard on input focus

I have a problem that i can't fix.
Keyboard doesn't show on input.focus() on IOS
searchMobileToggle.addEventListener('click', function() {
setTimeout(function(){
searchField.focus();
}, 300);
});
I've been looking for a solution with no result, i know this is a frequently unsolved question but i see NIKE (https://m.nike.com/fr/fr_fr/) and FOODSPRING (https://www.foodspring.fr/) doing it on mobile.
So i'm wondering how do they do ?
None of the other answers worked for me. I ended up looking into the Nike javascript code and this is what I came up with as a reusable function:
function focusAndOpenKeyboard(el, timeout) {
if(!timeout) {
timeout = 100;
}
if(el) {
// Align temp input element approximately where the input element is
// so the cursor doesn't jump around
var __tempEl__ = document.createElement('input');
__tempEl__.style.position = 'absolute';
__tempEl__.style.top = (el.offsetTop + 7) + 'px';
__tempEl__.style.left = el.offsetLeft + 'px';
__tempEl__.style.height = 0;
__tempEl__.style.opacity = 0;
// Put this temp element as a child of the page <body> and focus on it
document.body.appendChild(__tempEl__);
__tempEl__.focus();
// The keyboard is open. Now do a delayed focus on the target element
setTimeout(function() {
el.focus();
el.click();
// Remove the temp element
document.body.removeChild(__tempEl__);
}, timeout);
}
}
// Usage example
var myElement = document.getElementById('my-element');
var modalFadeInDuration = 300;
focusAndOpenKeyboard(myElement, modalFadeInDuration); // or without the second argument
Note that this is definitely a hacky solution, but the fact that Apple hasn't fixed this in so long justifies it.
I found a solution, click() didn't work, but i figured it out.
searchMobileToggle.addEventListener('click', function() {
if(mobileSearchblock.classList.contains('active')) {
searchField.setAttribute('autofocus', 'autofocus');
searchField.focus();
}
else {
searchField.removeAttribute('autofocus');
}
});
I was working with vue.js that was removing input autofocus attribute, when the component was loaded.
So i had it on click, but there was another problem, the autofocus only worked once, but combined with focus(), it now work all the time :)
Thanks for your help !
This really drives me/us crazy. It works fine on the Android phone, but something is disabled by the Apple developer. (I understand it's annoying to pop the keyboard when not necessary though).
I accidentally found out that the "popup" module from Semantic-UI fixes this magically.
Note that the solution works for SemanticUI (#semantic-ui team may tell what event makes this work)
Here are how I did:
const [search, setSearch] = useState(false);
const inputRef = useRef(null);
React.useEffect(() => {
if (search) {
inputRef.current.focus();
} else {
inputRef.current.blur();
}
}, [search]);
<div onClick={() => setSearch(true)}>
<Popup
content="Search for Swimmers and Time Standards."
offset={[-500, -1000]}
trigger={<Icon name="search" />}
/>
</div>
{search && <Input ref={inputRef} />}
As you see, I wrapped the trigger Icon with the Popup module, and hide the Popup content by setting the crazy offset. And then it magically works.
See the demo here: https://swimstandards.com/ (check it out on your iPhone)
Angular solution:
on button click we need to create temporary input, append to existing container (close to our input) and focus on it.
btnClicked() {
this.showModal = true;
this.searchBar = this.renderer2.selectRootElement('#searchBar', true);
// 2nd argument preserves existing content
// setting helper field and focusing on it
this.inputHelper = this.renderer2.createElement('input');
this.renderer2.appendChild(this.searchBar, this.inputHelper);
this.inputHelper.focus();
let event = new KeyboardEvent('touchstart',{'bubbles':true});
this.searchBarButton.nativeElement.dispatchEvent(event);
}
after modal/target input is shown, we move focus and remove temporary one:
initiateKeyboard() {
setTimeout(()=> {
this.searchBarInput.nativeElement.focus();
this.renderer2.removeChild(this.searchBar, this.inputHelper);
},180);
}
and template:
<div id="searchBar">
<input type="button" class="button is-link is-light" value="Search" (click)="btnClicked()" (touchstart)="initiateKeyboard()" #searchBarButton>
</div>
You just need to remember that iPhone may zoom screen, so you need to adjust parameters of temporary input.
working solution: https://inputfocus.vercel.app/
Worked in 2022 with ios 16!
OMG, I searched for so long and the above solution won't work for me.
Here is how it worked for me. I wrapped the input in a React FocusLock component. Check this package out: https://www.npmjs.com/package/react-focus-lock
Here is a small example:
<FocusLock>
<Input />
</FocusLock>
There is no legitimate way to do this since iOS kind of wants to only open the keyboard on a user interaction, however you can still achieve this with either using prompt() or using focus() from within a click() event it and will show up.

Categories