จะตรวจจับได้อย่างไรเมื่อมีการคลิกยกเลิกในการป้อนไฟล์


112

ฉันจะตรวจจับได้อย่างไรเมื่อผู้ใช้ยกเลิกอินพุตไฟล์โดยใช้อินพุตไฟล์ html

onChange ช่วยให้ฉันตรวจพบเมื่อพวกเขาเลือกไฟล์ แต่ฉันก็อยากทราบด้วยว่าพวกเขายกเลิกเมื่อใด (ปิดกล่องโต้ตอบเลือกไฟล์โดยไม่ต้องเลือกอะไรเลย)


นอกจากนี้คุณยังสามารถตรวจจับได้ว่ามีคนเลือกไฟล์แล้วลองเลือกส่ง แต่ยกเลิกคุณจะว่างเปล่าe.target.files
jcubic

คำตอบ:


45

แม้ว่าจะไม่ใช่วิธีแก้ปัญหาโดยตรง แต่ก็แย่ตรงที่มันใช้งานได้กับออนโฟกัสเท่านั้น (ต้องมีการบล็อกเหตุการณ์ที่ค่อนข้าง จำกัด ) คุณสามารถทำได้ดังต่อไปนี้:

document.body.onfocus = function(){ /*rock it*/ }

สิ่งที่ดีเกี่ยวกับสิ่งนี้คือคุณสามารถแนบ / แยกออกได้ทันเวลากับเหตุการณ์ไฟล์และดูเหมือนว่าจะทำงานได้ดีกับอินพุตที่ซ่อนอยู่ (ข้อได้เปรียบที่แน่นอนหากคุณใช้วิธีแก้ปัญหาด้วยภาพสำหรับประเภทอินพุตเริ่มต้นที่เส็งเคร็ง = ' ไฟล์'). หลังจากนั้นคุณต้องดูว่าค่าอินพุตเปลี่ยนไปหรือไม่

ตัวอย่าง:

var godzilla = document.getElementById('godzilla')

godzilla.onclick = charge

function charge()
{
    document.body.onfocus = roar
    console.log('chargin')
}

function roar()
{
    if(godzilla.value.length) alert('ROAR! FILES!')
    else alert('*empty wheeze*')
    document.body.onfocus = null
    console.log('depleted')
}

ดูการใช้งานจริง: http://jsfiddle.net/Shiboe/yuK3r/6/

น่าเศร้าที่ดูเหมือนว่าจะใช้ได้กับเบราว์เซอร์ webkit เท่านั้น อาจมีคนอื่นคิดหาวิธีแก้ปัญหา firefox / IE


addEventListener("focus",fn,{once: true})ฉันไม่อยากให้เหตุการณ์ที่มุ่งเน้นที่จะยิงทุกครั้งดังนั้นผมจึงอยากจะใช้ อย่างไรก็ตามฉันไม่สามารถยิงมันได้เลยโดยใช้document.body.addEventListener.. ฉันไม่รู้ว่าทำไม แต่ฉันได้ผลลัพธ์เดียวกันกับwindow.addEventListener.
WoodenKitty

3
สิ่งนี้ใช้ไม่ได้ใน Firefox และ Edge สำหรับฉัน อย่างไรก็ตามการฟังmousemoveเหตุการณ์window.document นอกเหนือจากการฟังfocusบนwindowดูเหมือนจะใช้งานได้ทุกที่ (อย่างน้อยในเบราว์เซอร์สมัยใหม่ฉันไม่สนใจเกี่ยวกับความพินาศในทศวรรษที่ผ่านมา) โดยทั่วไปสามารถใช้เหตุการณ์การโต้ตอบใด ๆ สำหรับงานนี้ที่ถูกบล็อกโดยกล่องโต้ตอบเปิดไฟล์ที่เปิดอยู่
John Weisz

@WoodenKitty คุณใช้สิ่งนี้โดยใช้ addEventListener ได้อย่างไร ฉันพยายามทำสิ่งนี้ในเชิงมุมและเอกสารไม่มีใครทำงานให้ฉันเช่นกัน
Terrance Jackson

สิ่งนี้ใช้ไม่ได้กับช่องป้อนข้อมูลที่ซ่อนอยู่
Sebastian

20

คุณทำไม่ได้

ผลลัพธ์ของกล่องโต้ตอบไฟล์จะไม่เปิดเผยต่อเบราว์เซอร์


เป็นไปได้หรือไม่ที่จะตรวจสอบว่าตัวเลือกไฟล์เปิดอยู่หรือไม่?
Ppp

1
@Ppp - ไม่เบราว์เซอร์ไม่ได้บอกคุณอย่างนั้น
Oded

6
"ผลลัพธ์ของกล่องโต้ตอบไฟล์ไม่ถูกเปิดเผยต่อเบราว์เซอร์" สิ่งนี้ไม่เป็นความจริงหากปิดกล่องโต้ตอบโดยการเลือกไฟล์
Trevor

3
การเปลี่ยนแปลงที่ทริกเกอร์โดยใช้กล่องโต้ตอบไฟล์จะถูกเปิดเผยในเบราว์เซอร์ ดูว่าไม่มีการเลือกไฟล์ใดและไม่มีการเลือกไฟล์หลังจากนั้นก็ไม่มีอะไรเปลี่ยนแปลงจึงไม่มีการchangeจัดส่งเหตุการณ์
John Weisz

1
นอกจากนี้หากไฟล์ถูกเลือกไว้แล้วและคุณเลือกไฟล์เดิมอีกครั้งจะไม่มีการchangeส่งเหตุการณ์สำหรับสิ่งนั้น
Patrick Roberts

16

ดังนั้นฉันจะโยนหมวกของฉันเป็นคำถามนี้ตั้งแต่ฉันคิดวิธีแก้ปัญหาใหม่ ฉันมี Progressive Web App ที่ช่วยให้ผู้ใช้สามารถจับภาพและวิดีโอและอัปโหลดได้ เราใช้ WebRTC เมื่อเป็นไปได้ แต่กลับไปใช้เครื่องมือเลือกไฟล์ HTML5 สำหรับอุปกรณ์ที่มีการรองรับน้อยกว่า * ไอ Safari *หากคุณกำลังทำงานโดยเฉพาะกับแอปพลิเคชันเว็บบนมือถือ Android / iOS ซึ่งใช้กล้องเนทีฟเพื่อถ่ายภาพ / วิดีโอโดยตรงนี่เป็นวิธีแก้ปัญหาที่ดีที่สุด

ปมของปัญหานี้คือเมื่อหน้าเว็บโหลดขึ้นมาfileคือnullแต่เมื่อผู้ใช้เปิดกล่องโต้ตอบและกด "ยกเลิก" สิ่งfileนั้นก็ยังคงอยู่nullจึงไม่ "เปลี่ยนแปลง" จึงไม่มีการทริกเกอร์เหตุการณ์ "เปลี่ยนแปลง" สำหรับเดสก์ท็อปสิ่งนี้ก็ไม่เลวร้ายนักเพราะ UI ของเดสก์ท็อปส่วนใหญ่ไม่ได้ขึ้นอยู่กับการรู้ว่าเมื่อใดมีการเรียกใช้การยกเลิก แต่ UI ของอุปกรณ์เคลื่อนที่ที่นำกล้องขึ้นมาเพื่อถ่ายภาพ / วิดีโอนั้นขึ้นอยู่กับการทราบเมื่อกดยกเลิก

เดิมทีฉันใช้document.body.onfocusเหตุการณ์เพื่อตรวจจับเมื่อผู้ใช้กลับมาจากเครื่องมือเลือกไฟล์และสิ่งนี้ใช้ได้กับอุปกรณ์ส่วนใหญ่ แต่ iOS 11.3 พังเพราะเหตุการณ์นั้นไม่ถูกทริกเกอร์

แนวคิด

วิธีแก้ปัญหาของฉันคือ * shudder * เพื่อวัดเวลาของ CPU เพื่อตรวจสอบว่าขณะนี้เพจอยู่ในพื้นหน้าหรือพื้นหลัง บนอุปกรณ์เคลื่อนที่เวลาในการประมวลผลจะถูกกำหนดให้กับแอปที่อยู่เบื้องหน้า เมื่อกล้องมองเห็นได้มันจะขโมยเวลาของ CPU และจัดลำดับความสำคัญของเบราว์เซอร์ สิ่งที่เราต้องทำคือวัดว่าหน้าเว็บของเราได้รับเวลาในการประมวลผลเท่าใดเมื่อกล้องเปิดตัวเวลาที่มีอยู่ของเราจะลดลงอย่างมาก เมื่อกล้องถูกปิด (ไม่ว่าจะยกเลิกหรืออย่างอื่น) เวลาที่มีอยู่ของเราก็เพิ่มขึ้นอย่างรวดเร็ว

การนำไปใช้

เราสามารถวัดระยะเวลาของ CPU โดยใช้setTimeout()เพื่อเรียกใช้การเรียกกลับเป็น X มิลลิวินาทีจากนั้นวัดระยะเวลาที่เรียกใช้จริง เบราว์เซอร์จะไม่เรียกใช้งานอย่างแน่นอนหลังจาก X มิลลิวินาที แต่ถ้ามันอยู่ใกล้พอสมควรเราจะต้องอยู่เบื้องหน้า หากเบราว์เซอร์อยู่ไกลมาก (ช้ากว่าที่ร้องขอมากกว่า 10 เท่า) เราจะต้องอยู่เบื้องหลัง การใช้งานขั้นพื้นฐานเป็นดังนี้:

function waitForCameraDismiss() {
  const REQUESTED_DELAY_MS = 25;
  const ALLOWED_MARGIN_OF_ERROR_MS = 25;
  const MAX_REASONABLE_DELAY_MS =
      REQUESTED_DELAY_MS + ALLOWED_MARGIN_OF_ERROR_MS;
  const MAX_TRIALS_TO_RECORD = 10;

  const triggerDelays = [];
  let lastTriggerTime = Date.now();

  return new Promise((resolve) => {
    const evtTimer = () => {
      // Add the time since the last run
      const now = Date.now();
      triggerDelays.push(now - lastTriggerTime);
      lastTriggerTime = now;

      // Wait until we have enough trials before interpreting them.
      if (triggerDelays.length < MAX_TRIALS_TO_RECORD) {
        window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
        return;
      }

      // Only maintain the last few event delays as trials so as not
      // to penalize a long time in the camera and to avoid exploding
      // memory.
      if (triggerDelays.length > MAX_TRIALS_TO_RECORD) {
        triggerDelays.shift();
      }

      // Compute the average of all trials. If it is outside the
      // acceptable margin of error, then the user must have the
      // camera open. If it is within the margin of error, then the
      // user must have dismissed the camera and returned to the page.
      const averageDelay =
          triggerDelays.reduce((l, r) => l + r) / triggerDelays.length
      if (averageDelay < MAX_REASONABLE_DELAY_MS) {
        // Beyond any reasonable doubt, the user has returned from the
        // camera
        resolve();
      } else {
        // Probably not returned from camera, run another trial.
        window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
      }
    };
    window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
  });
}

ฉันทดสอบสิ่งนี้ใน iOS และ Android เวอร์ชันล่าสุดโดยเปิดใช้งานกล้องเนทีฟโดยตั้งค่าแอตทริบิวต์ใน<input />องค์ประกอบ

<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />

สิ่งนี้ได้ผลดีกว่าที่ฉันคาดไว้มาก ดำเนินการทดลอง 10 ครั้งโดยขอให้เรียกใช้ตัวจับเวลาใน 25 มิลลิวินาที จากนั้นจะวัดระยะเวลาที่ใช้ในการเรียกใช้จริงและหากค่าเฉลี่ยของการทดลอง 10 ครั้งน้อยกว่า 50 มิลลิวินาทีเราจะถือว่าเราต้องอยู่เบื้องหน้าและกล้องก็หายไป หากมากกว่า 50 มิลลิวินาทีเราจะต้องอยู่เบื้องหลังและควรรอต่อไป

รายละเอียดเพิ่มเติมบางอย่าง

ฉันใช้setTimeout()มากกว่าsetInterval()เพราะหลังสามารถจัดคิวการเรียกใช้หลายครั้งซึ่งดำเนินการทันทีหลังจากกัน สิ่งนี้สามารถเพิ่มสัญญาณรบกวนในข้อมูลของเราได้อย่างมากดังนั้นฉันจึงติดอยู่setTimeout()แม้ว่าจะซับซ้อนกว่าเล็กน้อยในการทำเช่นนั้น

ตัวเลขเฉพาะเหล่านี้ใช้ได้ดีสำหรับฉันแม้ว่าฉันจะเคยเห็นอย่างน้อยหนึ่งครั้งที่ตรวจพบการเลิกใช้กล้องก่อนเวลาอันควร ฉันเชื่อว่าเป็นเพราะกล้องอาจเปิดได้ช้าและอุปกรณ์อาจทำการทดลอง 10 ครั้งก่อนที่จะกลายเป็นพื้นหลังจริง การเพิ่มการทดลองเพิ่มเติมหรือรอสัก 25-50 มิลลิวินาทีก่อนที่จะเริ่มฟังก์ชันนี้อาจเป็นวิธีแก้ปัญหาชั่วคราว

เดสก์ทอป

น่าเสียดายที่สิ่งนี้ใช้ไม่ได้กับเบราว์เซอร์เดสก์ท็อปจริงๆ ในทางทฤษฎีเคล็ดลับเดียวกันนี้เป็นไปได้เนื่องจากจัดลำดับความสำคัญของหน้าปัจจุบันเหนือหน้าที่มีพื้นหลัง อย่างไรก็ตามเดสก์ท็อปจำนวนมากมีทรัพยากรเพียงพอที่จะทำให้หน้าทำงานด้วยความเร็วเต็มที่แม้ว่าจะอยู่เบื้องหลังดังนั้นกลยุทธ์นี้จึงไม่ได้ผลในทางปฏิบัติ

ทางเลือกในการแก้ปัญหา

ทางเลือกหนึ่งที่ไม่ค่อยมีใครพูดถึงว่าฉันสำรวจคือการล้อเลียนกFileList. เราเริ่มต้นด้วยnullใน<input />และจากนั้นถ้าผู้ใช้เปิดกล้องและยกเลิกพวกเขากลับไปมาnullซึ่งไม่ได้มีการเปลี่ยนแปลงและไม่มีเหตุการณ์จะเรียก วิธีแก้ปัญหาหนึ่งคือการกำหนดไฟล์ดัมมี่ให้กับการ<input />เริ่มต้นหน้าดังนั้นการตั้งค่าเป็นnullเป็นการเปลี่ยนแปลงซึ่งจะทำให้เกิดเหตุการณ์ที่เหมาะสม

น่าเสียดายที่ไม่มีวิธีอย่างเป็นทางการในการสร้าง a FileListและ<input />องค์ประกอบนั้นต้องใช้FileListโดยเฉพาะและจะไม่ยอมรับค่าอื่นใดนอกจากnullนี้ โดยปกติแล้วFileListวัตถุไม่สามารถสร้างได้โดยตรง แต่จะเกี่ยวข้องกับปัญหาด้านความปลอดภัยเก่า ๆ ซึ่งดูเหมือนจะไม่เกี่ยวข้องอีกต่อไป วิธีเดียวที่จะได้รับข้อมูลจาก<input />องค์ประกอบภายนอกคือการใช้แฮ็กที่คัดลอกวางข้อมูลเพื่อปลอมเหตุการณ์คลิปบอร์ดซึ่งอาจมีFileListวัตถุ (โดยทั่วไปแล้วคุณจะแกล้งทำเป็นลากแล้วปล่อยไฟล์ - เหตุการณ์เว็บไซต์ของคุณ) สิ่งนี้เป็นไปได้ใน Firefox แต่ไม่ใช่สำหรับ iOS Safari ดังนั้นจึงไม่สามารถใช้ได้กับกรณีการใช้งานเฉพาะของฉัน

เบราว์เซอร์โปรด ...

ไม่จำเป็นต้องพูดว่านี่เป็นเรื่องน่าขัน ความจริงที่ว่าหน้าเว็บได้รับการแจ้งเตือนเป็นศูนย์ว่ามีการเปลี่ยนแปลงองค์ประกอบ UI ที่สำคัญนั้นเป็นเรื่องน่าหัวเราะ นี่เป็นข้อบกพร่องในข้อมูลจำเพาะเนื่องจากไม่เคยมีไว้สำหรับ UI การจับภาพสื่อแบบเต็มหน้าจอและในทางเทคนิคจะไม่เรียกเหตุการณ์ "เปลี่ยนแปลง"ไปตามข้อกำหนด

อย่างไรก็ตามผู้จำหน่ายเบราว์เซอร์โปรดรับรู้ถึงความเป็นจริงของสิ่งนี้ได้หรือไม่? สิ่งนี้สามารถแก้ไขได้ด้วยเหตุการณ์ "เสร็จสิ้น" ใหม่ซึ่งถูกเรียกใช้แม้ว่าจะไม่มีการเปลี่ยนแปลงใด ๆ เกิดขึ้นหรือคุณสามารถเรียก "เปลี่ยน" ได้เลย ใช่ว่าจะขัดกับข้อมูลจำเพาะ แต่เป็นเรื่องเล็กน้อยสำหรับฉันที่จะสรุปเหตุการณ์การเปลี่ยนแปลงในฝั่ง JavaScript แต่โดยพื้นฐานแล้วเป็นไปไม่ได้ที่จะสร้างเหตุการณ์ "เสร็จสิ้น" ของฉันเอง แม้แต่วิธีแก้ปัญหาของฉันก็เป็นเพียงการวิเคราะห์พฤติกรรมหากไม่มีการรับประกันสถานะของเบราว์เซอร์

ตามที่กล่าวมาแล้ว API นี้ไม่สามารถใช้งานได้โดยพื้นฐานสำหรับอุปกรณ์พกพาและฉันคิดว่าการเปลี่ยนแปลงเบราว์เซอร์ที่ค่อนข้างเรียบง่ายอาจทำให้สิ่งนี้ง่ายขึ้นอย่างไม่มีที่สิ้นสุดสำหรับนักพัฒนาเว็บ * ขั้นตอนจากกล่องสบู่ *



7

เมื่อคุณเลือกไฟล์และคลิกเปิด / ยกเลิกinputองค์ประกอบควรสูญเสียโฟกัสหรือที่เรียกblurว่า สมมติว่าค่าเริ่มต้นvalueของinputค่าว่างเปล่าค่าใด ๆ ที่ไม่ว่างในblurตัวจัดการของคุณจะระบุว่าตกลงและค่าว่างจะหมายถึงยกเลิก

อัปเดต: blurไม่ถูกทริกเกอร์เมื่อinputซ่อนอยู่ ดังนั้นไม่สามารถใช้เคล็ดลับนี้กับการอัปโหลดที่อิง IFRAME ได้เว้นแต่คุณต้องการแสดงไฟล์input.


3
ใน Firefox 10 เบต้าและ Chrome 16 blurจะไม่ถูกทริกเกอร์เมื่อคุณเลือกไฟล์ ยังไม่ได้ลองในเบราว์เซอร์อื่น
Blaise

1
ไม่ได้ผล - การเบลอเมื่อป้อนข้อมูลจะถูกเรียกทันทีหลังจากคลิก Chrome 63.0.3239.84 x64 บน Win7
Qwertiy

7
/* Tested on Google Chrome */
$("input[type=file]").bind("change", function() {
    var selected_file_name = $(this).val();
    if ( selected_file_name.length > 0 ) {
        /* Some file selected */
    }
    else {
        /* No file selected or cancel/close
           dialog button clicked */
        /* If user has select a file before,
           when they submit, it will treated as
           no file selected */
    }
});

6
คุณตรวจสอบว่าelseงบของคุณเคยได้รับการประเมินหรือไม่?
jayarjo

เท่าที่ฉันจำได้เมื่อฉันเขียนคำตอบนี้หากผู้ใช้เลือกไฟล์แล้วเลือกไฟล์อีกครั้ง (แต่จากนั้นยกเลิก) ไฟล์นั้นจะถือว่าไม่ได้เลือกไฟล์ฉันใช้การแจ้งเตือนที่ตัวแปร selected_file_name เพื่อทดสอบเท่านั้น อย่างไรก็ตามไม่ได้ทดสอบอีกต่อไปตั้งแต่นั้นมา
Rizky

4
ฉันสามารถพูดได้ว่าหลังจากการทดสอบข้ามเบราว์เซอร์บางครั้ง Firefox จะไม่เริ่มเหตุการณ์การเปลี่ยนแปลงเมื่อยกเลิกและไม่ล้างไฟล์ที่มีอยู่ที่เลือกหากเปิดขึ้นมาใหม่และยกเลิกการคลิกในครั้งที่สอง แปลกใช่มั้ย?
mix3d

7

วิธีแก้ปัญหาเหล่านี้ส่วนใหญ่ไม่ได้ผลสำหรับฉัน

ปัญหาคือคุณไม่มีทางรู้ว่าเหตุการณ์ใดจะถูกกระตุ้นมันclickคือหรือมันchange? คุณไม่สามารถสรุปคำสั่งใด ๆ ได้เนื่องจากอาจขึ้นอยู่กับการใช้งานของเบราว์เซอร์

อย่างน้อยก็ใน Opera และ Chrome (ปลาย 2015) clickจะถูกเรียกก่อน 'เติม' ป้อนข้อมูลกับไฟล์เพื่อให้คุณจะไม่ทราบความยาวของfiles.length != 0จนกว่าคุณจะชะลอการถูกเรียกหลังจากclickchange

นี่คือรหัส:

var inputfile = $("#yourid");

inputfile.on("change click", function(ev){
    if (ev.originalEvent != null){
        console.log("OK clicked");
    }
    document.body.onfocus = function(){
        document.body.onfocus = null;
        setTimeout(function(){
            if (inputfile.val().length === 0) console.log("Cancel clicked");
        }, 1000);
    };
});

หากคุณคลิกที่หน้า ... "ยกเลิกการคลิก" ... = | (ทดสอบกับ Chrome)
Eduardo Lucio

5

เพียงฟังเหตุการณ์คลิกเช่นกัน

ตามตัวอย่างของ Shiboe นี่คือตัวอย่าง jQuery:

var godzilla = $('#godzilla');
var godzillaBtn = $('#godzilla-btn');

godzillaBtn.on('click', function(){
    godzilla.trigger('click');
});

godzilla.on('change click', function(){

    if (godzilla.val() != '') {
        $('#state').html('You have chosen a Mech!');    
    } else {
        $('#state').html('Choose your Mech!');
    }

});

คุณสามารถดูการทำงานได้ที่นี่: http://jsfiddle.net/T3Vwz


1
ฉันทดสอบบน Chrome 51 มันใช้ไม่ได้ในครั้งแรกที่คุณเลือกไฟล์ วิธีแก้คือเพิ่มความล่าช้า: jsfiddle.net/7jfbmunh
Benoit Blanchon

ซอของคุณใช้ไม่ได้สำหรับฉัน - โดยใช้ Firefox 58.0.2
barrypicker

4

คุณสามารถยกเลิกได้หากคุณเลือกไฟล์เดียวกันกับก่อนหน้านี้และคุณคลิกยกเลิก: ในกรณีนี้

คุณสามารถทำได้ดังนี้:

<input type="file" id="myinputfile"/>
<script>
document.getElementById('myinputfile').addEventListener('change', myMethod, false);
function myMethod(evt) {
  var files = evt.target.files; 
  f= files[0];
  if (f==undefined) {
     // the user has clicked on cancel
  }
  else if (f.name.match(".*\.jpg")|| f.name.match(".*\.png")) {
     //.... the user has choosen an image file
     var reader = new FileReader();
     reader.onload = function(evt) { 
        try {
        myimage.src=evt.target.result;
            ...
         } catch (err) {
            ...
         }
     };
  }     
  reader.readAsDataURL(f);          
</script>

1
Thnkx @loveJs โพสต์นี้ช่วยฉันได้จริงๆ
VPK

4
ไม่แน่ถ้าผู้ใช้เลือกไฟล์เดียวกันมันจะไม่ยกเลิก = (
Tiago Peres França

13
onchange จะเริ่มทำงานเมื่อมีการเปลี่ยนแปลงเท่านั้น ดังนั้นหากไฟล์นั้นเป็นไฟล์เดียวกับก่อนหน้านี้ตัวฟัง onchange จะไม่เริ่มทำงาน
เชน

1
คุณสามารถล้างการเลือกอินพุต (input.value = '') หลังจากที่คุณตรวจพบการเปลี่ยนแปลงครั้งแรกเพื่อให้การเปลี่ยนแปลงครั้งที่สองเริ่มทำงานแม้ว่าผู้ใช้จะเลือกไฟล์เดียวกันอีกครั้ง
คริส

1
@CodeGems อนุญาตให้ตั้งค่า input.value โดยทางโปรแกรมเป็นสตริงว่างได้ แต่คุณคิดถูกที่ไม่อนุญาตให้ตั้งค่าเป็นค่าอื่น
คริส

4

วิธีที่ง่ายที่สุดคือตรวจสอบว่ามีไฟล์อยู่ในหน่วยความจำชั่วคราวหรือไม่ หากคุณต้องการรับเหตุการณ์การเปลี่ยนแปลงทุกครั้งที่ผู้ใช้คลิกที่อินพุตไฟล์คุณสามารถทริกเกอร์ได้

var yourFileInput = $("#yourFileInput");

yourFileInput.on('mouseup', function() {
    $(this).trigger("change");
}).on('change', function() {
    if (this.files.length) {
        //User chose a picture
    } else {
        //User clicked cancel
    }
});

2

วิธีแก้ปัญหาของ Shiboe จะเป็นวิธีที่ดีหากใช้งานได้กับ webkit บนอุปกรณ์เคลื่อนที่ แต่ก็ไม่เป็นเช่นนั้น สิ่งที่ฉันสามารถทำได้คือการเพิ่มตัวรับฟังเหตุการณ์ mousemove ให้กับวัตถุบางตัวในเวลาที่เปิดหน้าต่างป้อนไฟล์ดังนี้:

 $('.upload-progress').mousemove(function() {
        checkForFiles(this);
      });
      checkForFiles = function(me) {
        var filefield = $('#myfileinput');
        var files = filefield.get(0).files;
        if (files == undefined || files[0] == undefined) $(me).remove(); // user cancelled the upload
      };

เหตุการณ์ mousemove ถูกบล็อกจากเพจในขณะที่กล่องโต้ตอบไฟล์เปิดอยู่และเมื่อปิดแล้วจะตรวจสอบว่ามีไฟล์ใดในอินพุตไฟล์หรือไม่ ในกรณีของฉันฉันต้องการให้ตัวบ่งชี้กิจกรรมปิดกั้นสิ่งต่าง ๆ จนกว่าไฟล์จะถูกอัปโหลดดังนั้นฉันต้องการลบตัวบ่งชี้ของฉันเมื่อยกเลิกเท่านั้น

อย่างไรก็ตามสิ่งนี้ไม่สามารถแก้ได้สำหรับมือถือเนื่องจากไม่มีเมาส์ให้ขยับ วิธีแก้ปัญหาของฉันมีน้อยกว่าที่สมบูรณ์แบบ แต่ฉันคิดว่ามันดีพอ

       $('.upload-progress').bind('touchstart', function() {
        checkForFiles(this);
      });

ตอนนี้เรากำลังฟังการสัมผัสบนหน้าจอเพื่อทำการตรวจสอบไฟล์เดียวกัน ฉันค่อนข้างมั่นใจว่านิ้วของผู้ใช้จะวางบนหน้าจออย่างรวดเร็วหลังจากยกเลิกและปิดตัวบ่งชี้กิจกรรมนี้

นอกจากนี้คุณยังสามารถเพิ่มตัวบ่งชี้กิจกรรมในเหตุการณ์การเปลี่ยนแปลงอินพุตไฟล์ได้ แต่บนมือถือมักจะมีความล่าช้าสองสามวินาทีระหว่างการเลือกรูปภาพและการเริ่มทำงานของเหตุการณ์การเปลี่ยนแปลงดังนั้น UX จึงดีกว่ามากสำหรับตัวบ่งชี้กิจกรรมที่จะแสดงที่ เริ่มต้นกระบวนการ


2

ฉันพบว่า atribute นี้เรียบง่ายที่สุด

if ($('#selectedFile')[0].files.length > 1)
{
   // Clicked on 'open' with file
} else {
   // Clicked on 'cancel'
}

นี่selectedFileคือinput type=fileไฟล์.


นี่เป็นทางออกที่ดีสำหรับฉันยกเว้นว่าควรจะเป็น> = 1
Bron Thulke

2

ฉันรู้ว่านี่เป็นคำถามที่เก่ามาก แต่ในกรณีที่ช่วยใครบางคนฉันพบเมื่อใช้เหตุการณ์ onmousemove เพื่อตรวจจับการยกเลิกว่าจำเป็นต้องทดสอบเหตุการณ์ดังกล่าวตั้งแต่สองเหตุการณ์ขึ้นไปในช่วงเวลาสั้น ๆ เนื่องจากเบราว์เซอร์สร้างเหตุการณ์ onmousemove รายการเดียว (Chrome 65) ทุกครั้งที่มีการย้ายเคอร์เซอร์ออกจากหน้าต่างข้อความเลือกไฟล์และทุกครั้งที่ย้ายออกจากหน้าต่างหลักและกลับเข้ามาอีกครั้ง ควบคู่ไปกับการหมดเวลาในช่วงเวลาสั้น ๆ เพื่อรีเซ็ตตัวนับกลับไปที่ศูนย์จะทำงานได้ดี


1
ย่อหน้าแรกเป็นคำตอบที่ดีแม้ว่าจะแบ่งเป็นหลาย ๆ อันก็ได้เพื่อให้อ่านง่ายขึ้น แต่บรรทัด "SOAPBOX" ทั้งสองนี้ทำให้เป็นเส้นขอบไปยัง Non-Answer เนื่องจากคำถามไม่ได้อยู่ในคำตอบ คุณสามารถ reword / rephrase คำตอบของคุณสักหน่อย? ขอบคุณ.
Filnor

1

ในกรณีของฉันฉันต้องซ่อนปุ่มส่งในขณะที่ผู้ใช้เลือกรูปภาพ

นี่คือสิ่งที่ฉันคิดขึ้นมา:

$(document).on('click', '#image-field', function(e) {
  $('.submit-button').prop('disabled', true)
})
$(document).on('focus', '#image-field'), function(e) {
  $('.submit-button').prop('disabled', false)
})

#image-fieldคือตัวเลือกไฟล์ของฉัน เมื่อ Somenone คลิกฉันจะปิดการใช้งานปุ่มส่งแบบฟอร์ม ประเด็นคือเมื่อปิดกล่องโต้ตอบไฟล์ - ไม่ว่าพวกเขาจะเลือกไฟล์หรือยกเลิก - #image-fieldได้รับโฟกัสกลับมาดังนั้นฉันจึงฟังเหตุการณ์นั้น

อัปเดต

ฉันพบว่าสิ่งนี้ใช้ไม่ได้ในซาฟารีและ poltergeist / phantomjs นำข้อมูลนี้มาพิจารณาหากคุณต้องการนำไปใช้


1

การรวมโซลูชันของ Shiboe และ alx ฉันมีรหัสที่น่าเชื่อถือที่สุด:

var selector = $('<input/>')
   .attr({ /* just for example, use your own attributes */
      "id": "FilesSelector",
      "name": "File",
      "type": "file",
      "contentEditable": "false" /* if you "click" on input via label, this prevents IE7-8 from just setting caret into file input's text filed*/
   })
   .on("click.filesSelector", function () {
      /* do some magic here, e.g. invoke callback for selection begin */
      var cancelled = false; /* need this because .one calls handler once for each event type */
      setTimeout(function () {
         $(document).one("mousemove.filesSelector focusin.filesSelector", function () {
            /* namespace is optional */
            if (selector.val().length === 0 && !cancelled) {
               cancelled = true; /* prevent double cancel */
               /* that's the point of cancel,   */
            }
         });                    
      }, 1); /* 1 is enough as we just need to delay until first available tick */
   })
   .on("change.filesSelector", function () {
      /* do some magic here, e.g. invoke callback for successful selection */
   })
   .appendTo(yourHolder).end(); /* just for example */

โดยทั่วไปแล้วเหตุการณ์ mousemove จะหลอกลวง แต่ในกรณีที่ผู้ใช้คลิกและมากกว่า:

  • ยกเลิกกล่องโต้ตอบเปิดไฟล์โดยใช้ปุ่ม Escape (โดยไม่ต้องเลื่อนเมาส์) ทำการคลิกที่ถูกต้องอีกครั้งเพื่อเปิดกล่องโต้ตอบไฟล์อีกครั้ง ...
  • เปลี่ยนโฟกัสไปที่แอปพลิเคชั่นอื่น ๆ แทนที่จะกลับมาที่กล่องโต้ตอบเปิดไฟล์ของเบราว์เซอร์และปิดกว่าเปิดอีกครั้งผ่านปุ่ม Enter หรือ Space ...

... เราจะไม่ได้รับเหตุการณ์ mousemove ดังนั้นจึงไม่มีการยกเลิกการโทรกลับ ยิ่งไปกว่านั้นหากผู้ใช้ยกเลิกกล่องโต้ตอบที่สองและเลื่อนเมาส์เราจะได้รับการยกเลิกการโทรกลับ 2 ครั้ง โชคดีที่ jQuery focus เป็นพิเศษในเหตุการณ์ที่เกิดขึ้นในเอกสารทั้งสองกรณีช่วยให้เราหลีกเลี่ยงสถานการณ์ดังกล่าว ข้อ จำกัด เพียงอย่างเดียวคือถ้าหนึ่งบล็อก focusIn เหตุการณ์อย่างใดอย่างหนึ่ง


ฉันจับmousemoveเหตุการณ์documentระหว่างเลือกไฟล์ในกล่องโต้ตอบ OS ใน Chrome 56.0.2924.87 บน Ubuntu 16.10 ไม่มีปัญหาเมื่อไม่ได้เลื่อนเมาส์หรือใช้เพียงแป้นพิมพ์เพื่อเลือกไฟล์ ฉันต้องแทนที่mousemoveด้วยkeydownเพื่อตรวจจับการยกเลิกให้เร็วที่สุด แต่ไม่ "เร็วเกินไป" เมื่อกล่องโต้ตอบยังเปิดอยู่
Ferdinand Prantl

1

ฉันเห็นว่าคำตอบของฉันค่อนข้างล้าสมัย แต่ก็ไม่น้อยไปกว่ากัน ฉันประสบกับปัญหาเดียวกัน นี่คือทางออกของฉัน โค้ดที่มีประโยชน์ที่สุดคือรหัสของ KGA แต่มันใช้งานไม่ได้ทั้งหมดและค่อนข้างซับซ้อน แต่ฉันทำให้มันง่ายขึ้น

นอกจากนี้ตัวสร้างปัญหาหลักคือข้อเท็จจริงที่ว่าเหตุการณ์ 'การเปลี่ยนแปลง' ไม่ได้เกิดขึ้นทันทีหลังจากโฟกัสดังนั้นเราต้องรอสักครู่

"#appendfile" - ผู้ใช้รายใดคลิกเพื่อต่อท้ายไฟล์ใหม่ Hrefs รับเหตุการณ์โฟกัส

$("#appendfile").one("focusin", function () {
    // no matter - user uploaded file or canceled,
    // appendfile gets focus
    // change doesn't come instantly after focus, so we have to wait for some time
    // wrapper represents an element where a new file input is placed into
    setTimeout(function(){
        if (wrapper.find("input.fileinput").val() != "") {
            // user has uploaded some file
            // add your logic for new file here
        }
        else {
            // user canceled file upload
            // you have to remove a fileinput element from DOM
        }
    }, 900);
});

1

คุณสามารถตรวจจับสิ่งนี้ได้ในบางสถานการณ์เท่านั้น โดยเฉพาะใน chrome หากไฟล์ถูกเลือกไว้ก่อนหน้านี้แล้วกล่องโต้ตอบไฟล์ถูกคลิกและยกเลิกการคลิก Chrome จะล้างไฟล์และเริ่มการทำงานของเหตุการณ์ onChange

https://code.google.com/p/chromium/issues/detail?id=2508

ในสถานการณ์สมมตินี้คุณสามารถตรวจพบได้โดยจัดการกับเหตุการณ์ onChange และตรวจสอบคุณสมบัติไฟล์


1

สิ่งต่อไปนี้ดูเหมือนจะใช้ได้สำหรับฉัน (บนเดสก์ท็อป, windows):

var openFile = function (mimeType, fileExtension) {
    var defer = $q.defer();
    var uploadInput = document.createElement("input");
    uploadInput.type = 'file';
    uploadInput.accept = '.' + fileExtension + ',' + mimeType;

    var hasActivated = false;

    var hasChangedBeenCalled = false;
    var hasFocusBeenCalled = false;
    var focusCallback = function () {
        if (hasActivated) {
            hasFocusBeenCalled = true;
            document.removeEventListener('focus', focusCallback, true);
            setTimeout(function () {
                if (!hasChangedBeenCalled) {
                    uploadInput.removeEventListener('change', changedCallback, true);
                    defer.resolve(null);
                }
            }, 300);
        }
    };

    var changedCallback = function () {
        uploadInput.removeEventListener('change', changedCallback, true);
        if (!hasFocusBeenCalled) {
            document.removeEventListener('focus', focusCallback, true);
        }
        hasChangedBeenCalled = true;
        if (uploadInput.files.length === 1) {
            //File picked
            var reader = new FileReader();
            reader.onload = function (e) {
                defer.resolve(e.target.result);
            };
            reader.readAsText(uploadInput.files[0]);
        }
        else {
            defer.resolve(null);
        }
    };
    document.addEventListener('focus', focusCallback, true); //Detect cancel
    uploadInput.addEventListener('change', changedCallback, true); //Detect when a file is picked
    uploadInput.click();
    hasActivated = true;
    return defer.promise;
}

สิ่งนี้ใช้ angularjs $ q แต่คุณควรจะสามารถแทนที่ด้วยกรอบสัญญาอื่น ๆ ได้หากจำเป็น

ทดสอบบน IE11, Edge, Chrome, Firefox แต่ดูเหมือนว่าจะไม่ทำงานบน Chrome บนแท็บเล็ต Android เนื่องจากไม่ทำให้เหตุการณ์โฟกัสเกิดขึ้น


1

ช่องfile-type น่าหงุดหงิดไม่ตอบสนองต่อเหตุการณ์มากนัก (ภาพเบลอจะน่ารัก) ฉันเห็นผู้คนจำนวนมากแนะนำchangeวิธีแก้ปัญหาที่มีความเชี่ยวชาญและพวกเขาก็ถูกลดคะแนนลง

changeใช้งานได้ แต่มีข้อบกพร่องที่สำคัญ (เทียบกับสิ่งที่เราต้องการให้เกิดขึ้น)

เมื่อคุณโหลดหน้าที่มีฟิลด์ไฟล์ใหม่ให้เปิดกล่องแล้วกดยกเลิก ไม่มีอะไรเปลี่ยนแปลงอย่างน่าหงุดหงิด

สิ่งที่ฉันเลือกทำคือโหลดในสภาพที่มีรั้วรอบขอบชิด

  • ส่วนถัดไปของแบบฟอร์ม a section#after-imageในกรณีของฉันถูกซ่อนจากมุมมอง เมื่อมีfile fieldการเปลี่ยนแปลงปุ่มอัปโหลดจะปรากฏขึ้น เมื่ออัปโหลดสำเร็จsection#after-imageจะปรากฏขึ้น
  • หากผู้ใช้โหลดเปิดกล่องโต้ตอบไฟล์จากนั้นยกเลิกพวกเขาจะไม่เห็นปุ่มอัปโหลด
  • หากผู้ใช้เลือกไฟล์ปุ่มอัปโหลดจะแสดงขึ้น หากพวกเขาเปิดกล่องโต้ตอบและยกเลิกchangeเหตุการณ์จะถูกเรียกโดยการยกเลิกนี้และที่นั่นฉันสามารถ (และทำ) ซ่อนปุ่มอัปโหลดของฉันอีกครั้งจนกว่าจะเลือกไฟล์ที่เหมาะสม

ฉันโชคดีที่รัฐที่มีรั้วรอบขอบชิดนี้เป็นผู้ออกแบบฟอร์มของฉันอยู่แล้ว คุณไม่จำเป็นต้องใช้สไตล์เดียวกันเพียงแค่ซ่อนปุ่มอัปโหลดไว้ในตอนแรกและเมื่อมีการเปลี่ยนแปลงการตั้งค่าฟิลด์ที่ซ่อนอยู่หรือตัวแปรจาวาสคริปต์เป็นสิ่งที่คุณสามารถตรวจสอบได้เมื่อส่ง

ฉันพยายามเปลี่ยนค่าของไฟล์ [0] ก่อนที่จะโต้ตอบกับฟิลด์ สิ่งนี้ไม่ได้ทำอะไรเกี่ยวกับการเปลี่ยนแปลง

ใช่ได้changeผลอย่างน้อยก็ดีที่สุดเท่าที่เราจะได้รับ ฟิลด์ไฟล์มีความปลอดภัยด้วยเหตุผลที่ชัดเจน แต่เพื่อความไม่พอใจของนักพัฒนาที่มีเจตนาดี

มันไม่เหมาะกับจุดประสงค์ของฉัน แต่คุณอาจสามารถonclickโหลดพรอมต์คำเตือนได้ (ไม่ใช่alert()เพราะการประมวลผลเพจหยุดชะงัก) และซ่อนไว้หากมีการทริกเกอร์การเปลี่ยนแปลงและไฟล์ [0] เป็นโมฆะ หากไม่มีการทริกเกอร์การเปลี่ยนแปลง div จะยังคงอยู่ในสถานะ


0

มีวิธีแฮ็กในการดำเนินการนี้ (เพิ่มการโทรกลับหรือแก้ไขการใช้งานที่รอการตัดบัญชี / สัญญาแทนการalert()โทร):

var result = null;

$('<input type="file" />')
    .on('change', function () {
        result = this.files[0];
        alert('selected!');
    })
    .click();

setTimeout(function () {
    $(document).one('mousemove', function () {
        if (!result) {
            alert('cancelled');
        }
    });
}, 1000);

วิธีการทำงาน: ในขณะที่กล่องโต้ตอบการเลือกไฟล์เปิดอยู่เอกสารจะไม่ได้รับเหตุการณ์ตัวชี้เมาส์ มีความล่าช้า 1000 มิลลิวินาทีเพื่อให้กล่องโต้ตอบปรากฏขึ้นจริงและปิดกั้นหน้าต่างเบราว์เซอร์ ตรวจสอบใน Chrome และ Firefox (Windows เท่านั้น)

แต่นี่ไม่ใช่วิธีที่เชื่อถือได้ในการตรวจจับกล่องโต้ตอบที่ถูกยกเลิกแน่นอน แม้ว่าอาจปรับปรุงพฤติกรรม UI บางอย่างให้คุณ


ข้อเสียเปรียบประการหนึ่งคือโทรศัพท์และแท็บเล็ตพวกเขาไม่ได้ใช้เมาส์ตามปกติ!
ปีเตอร์

0

นี่คือวิธีแก้ปัญหาของฉันโดยใช้โฟกัสการป้อนไฟล์ (ไม่ใช้ตัวจับเวลาใด ๆ )

var fileInputSelectionInitiated = false;

function fileInputAnimationStart() {
    fileInputSelectionInitiated = true;
    if (!$("#image-selector-area-icon").hasClass("fa-spin"))
        $("#image-selector-area-icon").addClass("fa-spin");
    if (!$("#image-selector-button-icon").hasClass("fa-spin"))
        $("#image-selector-button-icon").addClass("fa-spin");
}

function fileInputAnimationStop() {
    fileInputSelectionInitiated = false;
    if ($("#image-selector-area-icon").hasClass("fa-spin"))
        $("#image-selector-area-icon").removeClass("fa-spin");
    if ($("#image-selector-button-icon").hasClass("fa-spin"))
        $("#image-selector-button-icon").removeClass("fa-spin");
}

$("#image-selector-area-wrapper").click(function (e) {
    $("#fileinput").focus();
    $("#fileinput").click();
});

$("#preview-image-wrapper").click(function (e) {
    $("#fileinput").focus();
    $("#fileinput").click();
});

$("#fileinput").click(function (e) {
    fileInputAnimationStart();
});

$("#fileinput").focus(function (e) {
    fileInputAnimationStop();
});

$("#fileinput").change(function(e) {
    // ...
}


การเรียกใช้ตัวอย่างข้อมูลของคุณช่วยให้ฉันError: { "message": "Uncaught SyntaxError: missing ) after argument list", "filename": "https://stacksnippets.net/js", "lineno": 51, "colno": 1 }
connexo

0

นี่เป็นวิธีที่ดีที่สุดแต่นี่คือตัวอย่างการทำงานของโซลูชันของฉันในการตรวจสอบว่าผู้ใช้อัปโหลดไฟล์หรือไม่และอนุญาตให้ดำเนินการต่อเมื่อได้อัปโหลดไฟล์เท่านั้น

โดยทั่วไปซ่อนContinue, Save, Proceedหรืออะไรก็ตามที่ปุ่มของคุณ จากนั้นใน JavaScript ให้คุณคว้าชื่อไฟล์ หากชื่อไฟล์ไม่มีค่าก็ไม่ต้องแสดงContinueปุ่ม หากมีค่าให้แสดงปุ่ม วิธีนี้ใช้ได้เช่นกันหากพวกเขาอัปโหลดไฟล์ในตอนแรกจากนั้นพยายามอัปโหลดไฟล์อื่นแล้วคลิกยกเลิก

นี่คือรหัส

HTML:

<div class="container">
<div class="row">
    <input class="file-input" type="file" accept="image/*" name="fileUpload" id="fileUpload" capture="camera">
    <label for="fileUpload" id="file-upload-btn">Capture or Upload Photo</label>
</div>
<div class="row padding-top-two-em">
    <input class="btn btn-success hidden" id="accept-btn" type="submit" value="Accept & Continue"/>
    <button class="btn btn-danger">Back</button>
</div></div>

JavaScript:

$('#fileUpload').change(function () {
    var fileName = $('#fileUpload').val();
    if (fileName != "") {
        $('#file-upload-btn').html(fileName);
        $('#accept-btn').removeClass('hidden').addClass('show');
    } else {
        $('#file-upload-btn').html("Upload File");
        $('#accept-btn').addClass('hidden');
    }
});

CSS:

.file-input {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    overflow: hidden;
    position: absolute;
    z-index: -1; 
}

.file-input + label {
    font-size: 1.25em;
    font-weight: normal;
    color: white;
    background-color: blue;
    display: inline-block;
    padding: 5px;
}

.file-input:focus + label,
.file-input + label:hover {
    background-color: red;
}

.file-input + label {
    cursor: pointer;
}

.file-input + label * {
    pointer-events: none;
}

สำหรับ CSS จำนวนมากนี้คือการทำให้เว็บไซต์และปุ่มสามารถเข้าถึงได้สำหรับทุกคน จัดแต่งปุ่มของคุณตามที่คุณต้องการ


0

นี่ไม่ได้ตอบคำถามของคุณอย่างแน่นอน สมมติฐานของฉันคือคุณมีสถานการณ์เมื่อคุณเพิ่มอินพุตไฟล์และเรียกใช้การเลือกไฟล์และหากผู้ใช้ยกเลิกคุณเพียงแค่ลบอินพุต

หากเป็นกรณีนี้: ทำไมต้องเพิ่มอินพุตไฟล์เปล่า?

สร้างได้ทันที แต่เพิ่มลงใน DOM เมื่อกรอกข้อมูลแล้วเท่านั้นเช่น:

    var fileInput = $("<input type='file' name='files' style='display: none' />");
    fileInput.bind("change", function() {
        if (fileInput.val() !== null) {
            // if has value add it to DOM
            $("#files").append(fileInput);
        }
    }).click();

ที่นี่ฉันสร้าง <input type = "file" /> ได้ทันทีเชื่อมโยงกับเหตุการณ์การเปลี่ยนแปลงจากนั้นเรียกใช้การคลิกทันที การเปลี่ยนแปลงจะเริ่มทำงานก็ต่อเมื่อผู้ใช้เลือกไฟล์และกดตกลงมิฉะนั้นอินพุตจะไม่ถูกเพิ่มลงใน DOM ดังนั้นจะไม่ถูกส่ง

ตัวอย่างการทำงานที่นี่: https://jsfiddle.net/69g0Lxno/3/


0

// ใช้โฮเวอร์แทนการเบลอ

var fileInput = $("#fileInput");

if (fileInput.is(":hover") { 

    //open
} else {

}

0

หากคุณต้องการ JQuery อยู่แล้วโซลูชันนี้อาจใช้งานได้ (นี่เป็นรหัสเดียวกับที่ฉันต้องการจริงๆในกรณีของฉันแม้ว่าการใช้ Promise จะเป็นเพียงการบังคับให้รหัสรอจนกว่าการเลือกไฟล์จะได้รับการแก้ไข):

await new Promise(resolve => {
    const input = $("<input type='file'/>");
    input.on('change', function() {
        resolve($(this).val());
    });
    $('body').one('focus', '*', e => {
        resolve(null);
        e.stopPropagation();
    });
    input.click();

});


0

มีวิธีแก้ปัญหาที่นำเสนอหลายอย่างในชุดข้อความนี้และความยากลำบากในการตรวจจับเมื่อผู้ใช้คลิกปุ่ม "ยกเลิก" ในช่องเลือกไฟล์เป็นปัญหาที่ส่งผลกระทบต่อคนจำนวนมาก

ความจริงก็คือไม่มีวิธีที่เชื่อถือได้ 100% ในการตรวจสอบว่าผู้ใช้คลิกปุ่ม "ยกเลิก"ในช่องเลือกไฟล์หรือไม่ แต่มีวิธีตรวจสอบอย่างน่าเชื่อถือว่าผู้ใช้เพิ่มไฟล์ลงในไฟล์อินพุตหรือไม่ นี่คือกลยุทธ์พื้นฐานของคำตอบนี้!

ฉันตัดสินใจเพิ่มคำตอบนี้เพราะเห็นได้ชัดว่าคำตอบอื่น ๆ ใช้ไม่ได้กับเบราว์เซอร์ส่วนใหญ่หรือรับประกันบนอุปกรณ์มือถือ

สั้น ๆ รหัสจะขึ้นอยู่กับ 3 จุด:

  1. ไฟล์อินพุตจะถูกสร้างขึ้นแบบไดนามิกใน "หน่วยความจำ" ใน js (เราไม่ได้เพิ่มลงใน "HTML" ในขณะนี้);
  2. หลังจากเพิ่มไฟล์แล้วไฟล์อินพุตจะถูกเพิ่มลงใน HTML มิฉะนั้นจะไม่มีอะไรเกิดขึ้น
  3. การลบไฟล์ทำได้โดยการลบไฟล์อินพุตออกจาก HTML โดยเหตุการณ์เฉพาะซึ่งหมายความว่าการ "แก้ไข" / "แก้ไข" ของไฟล์ทำได้โดยการลบไฟล์อินพุตเก่าและสร้างไฟล์ใหม่

เพื่อความเข้าใจที่ดียิ่งขึ้นโปรดดูโค้ดด้านล่างและหมายเหตุด้วย

[...]
<button type="button" onclick="addIptFl();">ADD INPUT FILE!</button>
<span id="ipt_fl_parent"></span>
[...]
function dynIptFl(jqElInst, funcsObj) {
    if (typeof funcsObj === "undefined" || funcsObj === "") {
        funcsObj = {};
    }
    if (funcsObj.hasOwnProperty("before")) {
        if (!funcsObj["before"].hasOwnProperty("args")) {
            funcsObj["before"]["args"] = [];
        }
        funcsObj["before"]["func"].apply(this, funcsObj["before"]["args"]);
    }
    var jqElInstFl = jqElInst.find("input[type=file]");

    // NOTE: Open the file selection box via js. By Questor
    jqElInstFl.trigger("click");

    // NOTE: This event is triggered if the user selects a file. By Questor
    jqElInstFl.on("change", {funcsObj: funcsObj}, function(e) {

        // NOTE: With the strategy below we avoid problems with other unwanted events
        // that may be associated with the DOM element. By Questor
        e.preventDefault();

        var funcsObj = e.data.funcsObj;
        if (funcsObj.hasOwnProperty("after")) {
            if (!funcsObj["after"].hasOwnProperty("args")) {
                funcsObj["after"]["args"] = [];
            }
            funcsObj["after"]["func"].apply(this, funcsObj["after"]["args"]);
        }
    });

}

function remIptFl() {

    // NOTE: Remove the input file. By Questor
    $("#ipt_fl_parent").empty();

}

function addIptFl() {

    function addBefore(someArgs0, someArgs1) {
    // NOTE: All the logic here happens just before the file selection box opens.
    // By Questor

        // SOME CODE HERE!

    }

    function addAfter(someArgs0, someArgs1) {
    // NOTE: All the logic here happens only if the user adds a file. By Questor

        // SOME CODE HERE!

        $("#ipt_fl_parent").prepend(jqElInst);

    }

    // NOTE: The input file is hidden as all manipulation must be done via js.
    // By Questor
    var jqElInst = $('\
<span>\
    <button type="button" onclick="remIptFl();">REMOVE INPUT FILE!</button>\
    <input type="file" name="input_fl_nm" style="display: block;">\
</span>\
');

    var funcsObj = {
        before: {
            func: addBefore,
            args: [someArgs0, someArgs1]
        },
        after: {
            func: addAfter,

            // NOTE: The instance with the input file ("jqElInst") could be passed
            // here instead of using the context of the "addIptFl()" function. That
            // way "addBefore()" and "addAfter()" will not need to be inside "addIptFl()",
            // for example. By Questor
            args: [someArgs0, someArgs1]

        }
    };
    dynIptFl(jqElInst, funcsObj);

}

ขอบคุณ! = ง


0

เราทำได้ในเชิงมุมเหมือนด้านล่าง

    1. เชื่อมโยงเหตุการณ์คลิกบนไฟล์ประเภทอินพุต
      1. แนบเหตุการณ์โฟกัสด้วยหน้าต่างและเพิ่มเงื่อนไขหาก uploadPanel เป็นจริงให้แสดงคอนโซล
      2. เมื่อคลิกที่ไฟล์ประเภทอินพุตค่าบูลีน uploadPanel จะเป็นจริง และกล่องโต้ตอบจะปรากฏขึ้น
      3. เมื่อกดปุ่มยกเลิกหรือ Esc คลิกแล้วกล่องโต้ตอบตู้ยาและคอนโซลจะปรากฏขึ้น

HTML

 <input type="file" formControlName="FileUpload" click)="handleFileInput($event.target.files)" />
          />

TS

this.uploadPanel = false;
handleFileInput(files: FileList) {
    this.fileToUpload = files.item(0);
    console.log("ggg" + files);
    this.uploadPanel = true;
  }



@HostListener("window:focus", ["$event"])
  onFocus(event: FocusEvent): void {
    if (this.uploadPanel == true) {
        console.log("cancel clicked")
        this.addSlot
        .get("FileUpload")
        .setValidators([
          Validators.required,
          FileValidator.validate,
          requiredFileType("png")
        ]);
      this.addSlot.get("FileUpload").updateValueAndValidity();
    }
  }

0

เพียงเพิ่มตัวฟัง 'เปลี่ยน' ในข้อมูลที่คุณป้อนซึ่งมีประเภทเป็นไฟล์ กล่าวคือ

<input type="file" id="file_to_upload" name="file_to_upload" />

ฉันใช้ jQuery เสร็จแล้วและเห็นได้ชัดว่าทุกคนสามารถใช้ valina JS ได้ (ตามข้อกำหนด)

$("#file_to_upload").change(function() {

            if (this.files.length) {

            alert('file choosen');

            } else {

            alert('file NOT choosen');

            }

    });

.change()จะไม่ถูกเรียกอย่างน่าเชื่อถือหากผู้ใช้คลิก "ยกเลิก" บนเครื่องมือเลือกไฟล์
Ben Wheeler

@benWheeler ฉันได้ตรวจสอบทั้งบน chrome และ firefox และใช้งานได้ แต่ไม่ได้ทดสอบบนเบราว์เซอร์อื่น
Naveed Ali

0
    enter code here
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div id="cancel01">
      <button>Cancel</button>
    </div>

    <div id="cancel02">
      <button>Cancel</button>
    </div>

    <div id="cancel03">
      <button>Cancel</button>
    </div>

    <form>
      <input type="file" name="name" placeholder="Name" />
    </form>

    <script>
      const nameInput = document.querySelector('input[type="file"]');

      /******
       *The below code if for How to detect when cancel is clicked on file input
       ******/
      nameInput.addEventListener('keydown', e => {
        /******
         *If the cancel button is clicked,then you should change the input file value to empty
         ******/

        if (e.key == 'Backspace' || e.code == 'Backspace' || e.keyCode == 8) {
          console.log(e);

          /******
           *The below code will delete the file path
           ******/
          nameInput.value = '';
        }
      });
    </script>
  </body>
</html>

คำตอบที่ใช้รหัสเท่านั้นถือว่ามีคุณภาพต่ำ: อย่าลืมให้คำอธิบายว่าโค้ดของคุณใช้ทำอะไรและแก้ปัญหาอย่างไร จะช่วยให้ผู้ถามและผู้อ่านในอนาคตทั้งสองอย่างหากคุณสามารถเพิ่มข้อมูลเพิ่มเติมในโพสต์ของคุณ ดูการอธิบายคำตอบแบบใช้รหัสทั้งหมด
Calos

0

โซลูชันสำหรับการเลือกไฟล์ที่มีอินพุตที่ซ่อนอยู่

หมายเหตุ: รหัสนี้ตรวจไม่พบการยกเลิกมีวิธีหลีกเลี่ยงความจำเป็นในการตรวจจับในกรณีทั่วไปที่ผู้คนพยายามตรวจพบ

ฉันมาที่นี่ในขณะที่กำลังมองหาวิธีแก้ปัญหาสำหรับการอัปโหลดไฟล์โดยใช้อินพุตที่ซ่อนอยู่ฉันเชื่อว่านี่เป็นสาเหตุที่พบบ่อยที่สุดในการค้นหาวิธีตรวจจับการยกเลิกการป้อนไฟล์ (กล่องโต้ตอบเปิดไฟล์ -> หากเลือกไฟล์แล้วให้เรียกใช้ รหัสหรือไม่ต้องทำอะไรเลย) นี่คือวิธีแก้ปัญหาของฉัน:

var fileSelectorResolve;
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');

fileSelector.addEventListener('input', function(){
   fileSelectorResolve(this.files[0]);
   fileSelectorResolve = null;
   fileSelector.value = '';
});

function selectFile(){
   if(fileSelectorResolve){
      fileSelectorResolve();
      fileSelectorResolve = null;
   }
   return new Promise(function(resolve){
      fileSelectorResolve = resolve;
      fileSelector.dispatchEvent(new MouseEvent('click'));
   });
}

ตัวอย่างการใช้งาน:

โปรดทราบว่าหากไม่มีการเลือกไฟล์บรรทัดแรกจะกลับมาเพียงครั้งเดียวเท่านั้นที่selectFile()ถูกเรียกอีกครั้ง (หรือหากคุณเรียกfileSelectorResolve()จากที่อื่น)

async function logFileName(){
   const file = await selectFile();
   if(!file) return;
   console.log(file.name);
}

ตัวอย่างอื่น:

async function uploadFile(){
   const file = await selectFile();
   if(!file) return;

   // ... make an ajax call here to upload the file ...
}

ใน chrome 83.0 ฉันไม่ได้รับการติดต่อกลับสำหรับเหตุการณ์การป้อนข้อมูลเมื่อผู้ใช้ยกเลิกและไม่ได้ลองครั้งที่ 2
Soylent Graham

1
@SoylentGraham ใช่เนื่องจากรหัสนี้ตรวจไม่พบการยกเลิกจึงมีวิธีหลีกเลี่ยงความจำเป็นในการตรวจจับในกรณีทั่วไปที่ผู้คนพยายามตรวจจับ เนื่องจากฉันไม่ชัดเจนเกี่ยวกับเรื่องนี้ฉันจึงเพิ่มความคิดเห็นนี้ในคำตอบของฉัน
มันฝรั่ง

ฉันคิดว่าฉันไม่ดีฉันอ่านคำตอบของคุณผิดเนื่องจาก "บอกว่าผู้ใช้ยกเลิกการลองครั้งที่ 2"
Soylent Graham

-1

คุณสามารถสร้าง jquery change listener ในช่องป้อนข้อมูลและตรวจพบว่าผู้ใช้ยกเลิกหรือปิดหน้าต่างอัปโหลดโดยใช้ค่าของฟิลด์

นี่คือตัวอย่าง:

//when upload button change
$('#upload_btn').change(function(){
    //get uploaded file
    var file = this.files[0];

    //if user choosed a file
    if(file){
        //upload file or perform your desired functiuonality
    }else{
        //user click cancel or close the upload window
    }
});

ไม่รู้ว่าทำไมสิ่งนี้จึงถูกลดลง - นี่คือสิ่งที่ฉันกำลังมองหา ขอบคุณ! 🙂
user1274820
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.