-1

I have file input that is added by a third party plugin, the uploaded file has a custom preview, which i can click on a remove button within the preview element to clear the input, and this preview is injected to the Dom uppon file addition. when i try to detect the action of clearing the input , neither change nor ìnput events are triggered.

Please note that i could normally listen to the change if file has been added only.

I have tried this:

const fileInput = document.getElementById('thumb');
        
fileInput.addEventListener('input', (e) => {
if( fileInput.files.length > 0 )
{
    const file = fileInput.files[0]; // Get the first selected file
    const blobURL = URL.createObjectURL(file);
                
}else{
    console.log('removed');
}
            
            
});

And sure i tried to replace input with change but no result

Mohamed Omar
  • 453
  • 5
  • 16
  • 1
    are you sure about the element you are targeting for the event listener? how can you be? Are you trying to intercept specifically the _"remove button within the preview element"_? how are you sure it's `#thumb`? and can't you just listen to the `click` event? – Diego D Feb 20 '23 at 08:01
  • Sorry, i cannot get your point. the input id is `thumb` and i could listen to the change or input events if file has been added, but i can not if cleared by clicking on the button on preview. If you try to tel that why not i listen to the click on the remove, so it is the hardest way because the preview is injected so shall depend on mutation observer i guess. – Mohamed Omar Feb 20 '23 at 08:10
  • I don't think file inputs can be cleared. From my experience, the cleanest way is to remove them from the DOM and then inject a cloned file input back. `function clearFileInput(ctrl) { try { ctrl.value = null; } catch (ex) {} if (ctrl.value) ctrl.parentNode.replaceChild(ctrl.cloneNode(true), ctrl); }` – IVO GELOV Feb 20 '23 at 08:26
  • 1
    I think I see the problem now. As far as I know, the `.value` property cannot strictly be observed like you would with a `MutationObserver` because it's a property of the object and not the attribute being observed. There's a SO qa [here](https://stackoverflow.com/questions/32383349/detect-input-value-change-with-mutationobserver) better explaining that. I still think it would be easier to address the remove button in your problem... but yet you can try to ovverride the `value` prop like explained in that link I shared – Diego D Feb 20 '23 at 08:26
  • @IVOGELOV thanks but i am trying to detect the change, not trying to clear the input – Mohamed Omar Feb 20 '23 at 08:29
  • @DiegoD Thanks i will read through. thank you – Mohamed Omar Feb 20 '23 at 08:31
  • `change` and `input` events are only generated as a result of user action - or forcibly emitted with `createEvent()`. As Diego suggested your best bet is to make the "remove" button emit a signal and then subsribe for that signal. – IVO GELOV Feb 20 '23 at 08:33

2 Answers2

0

Since you are having problems addressing the remove button and listen for its click event, and since a regular MutationObserver would be listening to the change of an attribute and not strictly an object's property value being changed,

you may try redefining the setter for the value property so that every time it will be changed programmatically, it will also perform your code.

You will find further details here (among the posted answers):

Detect input value change with MutationObserver

Here in this demo there's both the change event listener and the new property setter defined. When you'll try to clear the input using the corresponding button, the attempt to change its value property will be intercepted printing the value was changed programmatically! on console:

//adds a canonic change event handler to the #thumb element
document.getElementById('thumb')
  .addEventListener('change',()=>{
    console.log('file input has changed!');
  });
  
//clear the input file programmatically (when the corresponding button is pressed)
function clearFile(){
  document.getElementById('thumb').value = "";  
}

//on document ready
document.addEventListener('DOMContentLoaded',()=>{

  //overrides the setter for the value property of the #thumb element
  
  const fileInput = document.getElementById('thumb');  
  const originalSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;  
  Object.defineProperty(document.getElementById('thumb'), "value", {
      set:  function (newValue) {
        //so that it will print on console..
        console.log('the value was changed programmatically!');        
        //and perform its original designation
        return originalSetter.call(this, newValue);
      }
  });

});
<input type="file" id="thumb">
<button onclick="clearFile();">CLEAR PROGRAMMATICALLY</button>
Diego D
  • 6,156
  • 2
  • 17
  • 30
  • This would work if the button is rendered on the dom load but mine is injected so the js can't feel it. – Mohamed Omar Feb 20 '23 at 08:52
  • you shared a very narrow part of your problem in the question... but as stated you were expected to have the `#thumb` element available -> `const fileInput = document.getElementById('thumb');`. Now you add that also that element does not exist. Since the page is supposed to be under your control you should know when such element is available.. you have a third party library that you described as falling from sky... but who is calling it? shouldn't you have a statement somewhere that initalize the component? – Diego D Feb 20 '23 at 08:56
  • anyway to better clarify... _"the js can't feel it"_ it's pretty uncorrect.. it would be better stated as: the element is not yet available on document ready. But anyway, usually third party libraries are expected to have a function that you will call against an already existing raw element (see libraries for tables, dropdowns, input elements...). I'm quite sure you should have the raw element there in the html since the beginning. The question is unclear so far to give any further suggestion – Diego D Feb 20 '23 at 09:01
  • Thanks a lot, you have given me some hints i will try to follow. – Mohamed Omar Feb 20 '23 at 09:04
  • I gave you the solution! not hints! :D it would be appreciated if before asking a question you have the problem clear otherwise the solution is garbage as the question itself. – Diego D Feb 20 '23 at 09:06
  • I think you misunderstood me, because i have mentioned earlier on the first comment of yours, that the preview and the clear button have been injected which would make me use a mutation server to detect the injection, but your answer depends on other criteria. Any way i am sorry if i didn't explain well – Mohamed Omar Feb 20 '23 at 09:11
  • I did my best to abstract the problem at its furthest and couldn't go any far than that unfortunately. I was aware of that detail that some elements are injected but yet you talked about the preview button and the clear button. Not the input element being the element I'm listening for change. The MutationObserver must be attached to an existing element anyway and yet you are stuck again because you can't explain where is this `#thumb` element you stated in the question as available (according to the js you shared). Anyway I explained it wouldn't work. – Diego D Feb 20 '23 at 09:15
0

The solution I'm thinking of is to detect changes in the value of the input file by using a Javascript setInterval every 1 ms.

let fileInput = document.getElementById('thumb');
let check = document.getElementById('check');
let clear = document.getElementById('clear');
let currentValue;
let lastValue = '';

setInterval(()=>{
  currentValue = fileInput.value;
  if (lastValue !== currentValue && currentValue === '') {
    console.log('interval: file removed');
  } else if (lastValue !== currentValue && currentValue !== '') {
    console.log('interval: file added');
  }
  lastValue = currentValue;
},1);

check.onclick = () => {
  if(fileInput.files.length > 0) {
      const file = fileInput.files[0]; // Get the first selected file
      const blobURL = URL.createObjectURL(file);
      console.log(blobURL);
      console.log('onclick: file is exist');
  } else {
      console.log('onclick: file is empty');
  }
}

clear.onclick = () => {
  fileInput.value = '';
}
<input type="file" id="thumb">
<button id="check">Check</button>
<button id="clear">Clear</button>
Jordy
  • 1,802
  • 2
  • 6
  • 25